import { create } from 'zustand'
import { temporal } from 'zundo'
import { shallow } from 'zustand/shallow'
import { immer } from 'zustand/middleware/immer'
import type { Tree, TreeStructure, TreeCreate, TreeUpdate, NodeType, TreeMarkdownValidationError, TreeMarkdownValidation } from '@/types'
import { treeStructureToMarkdownPreview } from '@/lib/treeMarkdownSync'
import { safeGetItem, safeSetItem, safeRemoveItem } from '@/lib/utils'

// Throttle helper: captures first call immediately, then throttles subsequent calls
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function throttle<T extends (...args: any[]) => void>(fn: T, ms: number): T {
  let lastCall = 0
  let timeout: ReturnType<typeof setTimeout> | null = null
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return ((...args: any[]) => {
    const now = Date.now()
    if (now - lastCall >= ms) {
      lastCall = now
      fn(...args)
    } else {
      // Schedule a trailing call to capture the final state
      if (timeout) clearTimeout(timeout)
      timeout = setTimeout(() => {
        lastCall = Date.now()
        fn(...args)
      }, ms - (now - lastCall))
    }
  }) as T
}

// Validation error interface
export interface ValidationError {
  nodeId?: string
  field?: string
  message: string
  severity: 'error' | 'warning'
}

// Draft storage key
const DRAFT_STORAGE_KEY = 'tree-editor-draft'

// Check if a tree is effectively empty (just initialized, no real content)
const isEmptyTree = (tree: TreeStructure | null): boolean => {
  if (!tree) return true
  if (tree.question && tree.question.trim()) return false
  if (tree.children && tree.children.length > 0) return false
  return true
}

// Starter template for new trees in Code Mode — all @refs are valid within the template
const CODE_MODE_STARTER_TEMPLATE = `---
name: My New Tree
description: A troubleshooting decision tree
---

---
id: root
type: decision
---
# What type of issue is the user experiencing?

> Select the category that best matches the reported problem

- [A] Option A \u2192 @option_a_action
- [B] Option B \u2192 @option_b_action

---
id: option_a_action
type: action
parent: root
---
## Investigate Option A

Describe the investigation steps here.

\`\`\`commands
example-command --flag
\`\`\`

**Expected:** Describe expected results here

\u2192 @resolution

---
id: option_b_action
type: action
parent: root
---
## Investigate Option B

Describe the investigation steps here.

\u2192 @resolution

---
id: resolution
type: solution
parent: root
---
## Resolution

1. Document findings
2. Apply the fix
3. Verify the issue is resolved
`

// Helper to generate unique IDs
const generateId = () => crypto.randomUUID()

// Helper to find a node in the tree structure (exported for drag validation)
export const findNodeInTree = (
  nodeId: string,
  structure: TreeStructure | null
): TreeStructure | null => {
  if (!structure) return null
  if (structure.id === nodeId) return structure
  if (structure.children) {
    for (const child of structure.children) {
      const found = findNodeInTree(nodeId, child)
      if (found) return found
    }
  }
  return null
}

/** Collect all nodes in the tree as a flat list with depth info. */
export function collectAllNodesFlat(
  root: TreeStructure | null
): Array<{ id: string; label: string; type: string; depth: number }> {
  if (!root) return []
  const result: Array<{ id: string; label: string; type: string; depth: number }> = []

  function walk(node: TreeStructure, depth: number) {
    const label = node.type === 'decision'
      ? (node.question || 'Untitled Decision')
      : (node.title || `Untitled ${node.type}`)
    result.push({ id: node.id, label, type: node.type, depth })
    node.children?.forEach(child => walk(child, depth + 1))
  }

  walk(root, 0)
  return result
}

// Helper to find parent of a node
const findParentNode = (
  nodeId: string,
  structure: TreeStructure | null,
  parent: TreeStructure | null = null
): TreeStructure | null => {
  if (!structure) return null
  if (structure.id === nodeId) return parent
  if (structure.children) {
    for (const child of structure.children) {
      const found = findParentNode(nodeId, child, structure)
      if (found) return found
    }
  }
  return null
}

// Helper to get all node IDs
const getAllNodeIds = (structure: TreeStructure | null): string[] => {
  if (!structure) return []
  const ids = [structure.id]
  if (structure.children) {
    for (const child of structure.children) {
      ids.push(...getAllNodeIds(child))
    }
  }
  return ids
}

// Helper to deep clone a node
const deepCloneNode = (node: TreeStructure): TreeStructure => {
  const clone: TreeStructure = { ...node, id: generateId() }

  // Update title/question to indicate it's a copy
  if (clone.question) {
    clone.question = `${clone.question} (Copy)`
  } else if (clone.title) {
    clone.title = `${clone.title} (Copy)`
  }

  // Clone options with new IDs
  if (clone.options) {
    clone.options = clone.options.map(opt => ({
      ...opt,
      id: generateId(),
      next_node_id: '' // Clear references - user must reassign
    }))
  }

  // Clear next_node_id - user must reassign
  if (clone.next_node_id) {
    clone.next_node_id = ''
  }

  // Clone children recursively
  if (clone.children) {
    clone.children = clone.children.map(child => deepCloneNode(child))
  }

  return clone
}

interface TreeEditorState {
  // Tree data
  treeId: string | null // null for new tree
  name: string
  description: string
  category: string
  categoryId: string | null
  tags: string[]
  isPublic: boolean
  treeStructure: TreeStructure | null
  originalTree: Tree | null // For comparison in edit mode

  // UI state
  selectedNodeId: string | null
  isDirty: boolean
  isLoading: boolean
  isSaving: boolean
  validationErrors: ValidationError[]

  // Code Mode state
  editorMode: 'form' | 'code'
  markdownSource: string | null
  markdownValidationErrors: TreeMarkdownValidationError[]
  isMarkdownValid: boolean
  isValidating: boolean
  lastValidTreeFromMarkdown: TreeStructure | null

  // Auto-save state
  lastSavedAt: Date | null
  draftSavedAt: Date | null
  hasDraft: boolean

  // Actions - Initialization
  initNewTree: () => void
  loadTree: (tree: Tree) => void
  loadDraft: () => boolean
  discardDraft: () => void
  reset: () => void

  // Actions - Metadata
  setName: (name: string) => void
  setDescription: (description: string) => void
  setCategory: (category: string) => void
  setCategoryId: (categoryId: string | null) => void
  setTags: (tags: string[]) => void
  addTag: (tag: string) => void
  removeTag: (tag: string) => void
  setIsPublic: (isPublic: boolean) => void

  // Actions - Node CRUD
  addNode: (parentId: string | null, type: NodeType, insertIndex?: number) => string
  updateNode: (nodeId: string, updates: Partial<TreeStructure>) => void
  deleteNode: (nodeId: string) => void
  duplicateNode: (nodeId: string) => string | null

  // Actions - Node ordering
  reorderNodes: (parentId: string, fromIndex: number, toIndex: number) => void
  moveNode: (nodeId: string, targetParentId: string, targetIndex: number) => void
  reorderOptions: (nodeId: string, fromIndex: number, toIndex: number) => void

  // Actions - Selection
  selectNode: (nodeId: string | null) => void

  // Actions - Validation
  validate: () => ValidationError[]
  clearValidation: () => void

  // Actions - Save/Draft
  autoSaveDraft: () => void
  markSaved: () => void
  getTreeForSave: () => TreeCreate | TreeUpdate

  // Actions - Code Mode
  setEditorMode: (mode: 'form' | 'code') => void
  setMarkdownSource: (markdown: string) => void
  setMarkdownValidationResult: (result: TreeMarkdownValidation) => void
  syncMarkdownToTree: () => void
  syncTreeToMarkdown: () => void

  // Actions - State
  setLoading: (loading: boolean) => void
  setSaving: (saving: boolean) => void

  // Helpers
  findNode: (nodeId: string) => TreeStructure | null
  getAllNodeIds: () => string[]
  getAvailableTargetNodes: (excludeNodeId?: string) => Array<{ id: string; label: string; type: NodeType }>
}

// Create store with immer and temporal (undo/redo) middleware
export const useTreeEditorStore = create<TreeEditorState>()(
  temporal(
    immer((set, get) => ({
      // Initial state
      treeId: null,
      name: '',
      description: '',
      category: '',
      categoryId: null,
      tags: [],
      isPublic: false,
      treeStructure: null,
      originalTree: null,
      selectedNodeId: null,
      isDirty: false,
      isLoading: false,
      isSaving: false,
      validationErrors: [],
      editorMode: 'form',
      markdownSource: null,
      markdownValidationErrors: [],
      isMarkdownValid: true,
      isValidating: false,
      lastValidTreeFromMarkdown: null,
      lastSavedAt: null,
      draftSavedAt: null,
      hasDraft: false,

      // Check for existing draft on init
      initNewTree: () => {
        const hasDraft = safeGetItem(DRAFT_STORAGE_KEY) !== null
        set((state) => {
          state.treeId = null
          state.name = ''
          state.description = ''
          state.category = ''
          state.categoryId = null
          state.tags = []
          state.isPublic = false
          state.treeStructure = {
            id: 'root',
            type: 'decision',
            question: '',
            options: [],
            children: []
          }
          state.originalTree = null
          state.selectedNodeId = 'root'
          state.isDirty = false
          state.isLoading = false
          state.isSaving = false
          state.validationErrors = []
          state.editorMode = 'form'
          state.markdownSource = null
          state.markdownValidationErrors = []
          state.isMarkdownValid = true
          state.isValidating = false
          state.lastValidTreeFromMarkdown = null
          state.lastSavedAt = null
          state.draftSavedAt = null
          state.hasDraft = hasDraft
        })
      },

      loadTree: (tree: Tree) => {
        set((state) => {
          state.treeId = tree.id
          state.name = tree.name
          state.description = tree.description || ''
          state.category = tree.category || ''
          state.categoryId = tree.category_id || null
          state.tags = tree.tags || []
          state.isPublic = tree.is_public || false
          state.treeStructure = tree.tree_structure
          state.originalTree = tree
          state.selectedNodeId = tree.tree_structure?.id || null
          state.isDirty = false
          state.isLoading = false
          state.validationErrors = []
          state.lastSavedAt = new Date()
          state.draftSavedAt = null
          state.hasDraft = false
        })
      },

      loadDraft: () => {
        const draftJson = safeGetItem(DRAFT_STORAGE_KEY)
        if (!draftJson) return false

        try {
          const draft = JSON.parse(draftJson)
          set((state) => {
            state.treeId = draft.treeId || null
            state.name = draft.name || ''
            state.description = draft.description || ''
            state.category = draft.category || ''
            state.categoryId = draft.categoryId || null
            state.tags = draft.tags || []
            state.isPublic = draft.isPublic || false
            state.treeStructure = draft.treeStructure || null
            state.isDirty = true
            state.draftSavedAt = draft.savedAt ? new Date(draft.savedAt) : null
            state.hasDraft = false
          })
          return true
        } catch {
          safeRemoveItem(DRAFT_STORAGE_KEY)
          return false
        }
      },

      discardDraft: () => {
        safeRemoveItem(DRAFT_STORAGE_KEY)
        set((state) => {
          state.hasDraft = false
        })
      },

      reset: () => {
        set((state) => {
          state.treeId = null
          state.name = ''
          state.description = ''
          state.category = ''
          state.categoryId = null
          state.tags = []
          state.isPublic = false
          state.treeStructure = null
          state.originalTree = null
          state.selectedNodeId = null
          state.isDirty = false
          state.isLoading = false
          state.isSaving = false
          state.validationErrors = []
          state.editorMode = 'form'
          state.markdownSource = null
          state.markdownValidationErrors = []
          state.isMarkdownValid = true
          state.isValidating = false
          state.lastValidTreeFromMarkdown = null
          state.lastSavedAt = null
          state.draftSavedAt = null
          state.hasDraft = false
        })
      },

      // Metadata actions
      setName: (name: string) => {
        set((state) => {
          state.name = name
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      setDescription: (description: string) => {
        set((state) => {
          state.description = description
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      setCategory: (category: string) => {
        set((state) => {
          state.category = category
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      setCategoryId: (categoryId: string | null) => {
        set((state) => {
          state.categoryId = categoryId
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      setTags: (tags: string[]) => {
        set((state) => {
          state.tags = tags
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      addTag: (tag: string) => {
        set((state) => {
          if (!state.tags.includes(tag)) {
            state.tags.push(tag)
            state.isDirty = true
          }
        })
        get().autoSaveDraft()
      },

      removeTag: (tag: string) => {
        set((state) => {
          state.tags = state.tags.filter(t => t !== tag)
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      setIsPublic: (isPublic: boolean) => {
        set((state) => {
          state.isPublic = isPublic
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      // Node CRUD
      addNode: (parentId: string | null, type: NodeType, insertIndex?: number) => {
        const newId = generateId()

        const newNode: TreeStructure = {
          id: newId,
          type,
          ...(type === 'decision' && {
            question: '',
            options: [],
            children: []
          }),
          ...(type === 'action' && {
            title: '',
            description: ''
          }),
          ...(type === 'solution' && {
            title: '',
            description: ''
          })
        }

        set((state) => {
          if (!parentId) {
            // Adding as root
            state.treeStructure = newNode
          } else {
            // Find parent and add to children
            const parent = findNodeInTree(parentId, state.treeStructure)
            if (parent) {
              if (!parent.children) {
                parent.children = []
              }
              if (insertIndex !== undefined && insertIndex >= 0) {
                parent.children.splice(insertIndex, 0, newNode)
              } else {
                parent.children.push(newNode)
              }
            }
          }
          state.selectedNodeId = newId
          state.isDirty = true
        })

        get().autoSaveDraft()
        return newId
      },

      updateNode: (nodeId: string, updates: Partial<TreeStructure>) => {
        set((state) => {
          const node = findNodeInTree(nodeId, state.treeStructure)
          if (node) {
            Object.assign(node, updates)
            state.isDirty = true
          }
        })
        get().autoSaveDraft()
      },

      deleteNode: (nodeId: string) => {
        if (nodeId === 'root') {
          // Don't allow deleting root, just clear it
          set((state) => {
            if (state.treeStructure) {
              state.treeStructure.question = ''
              state.treeStructure.options = []
              state.treeStructure.children = []
            }
            state.isDirty = true
          })
          get().autoSaveDraft()
          return
        }

        set((state) => {
          const parent = findParentNode(nodeId, state.treeStructure)
          if (parent && parent.children) {
            const index = parent.children.findIndex(c => c.id === nodeId)
            if (index !== -1) {
              parent.children.splice(index, 1)
            }
          }

          // Clear selection if deleted node was selected
          if (state.selectedNodeId === nodeId) {
            state.selectedNodeId = parent?.id || 'root'
          }

          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      duplicateNode: (nodeId: string) => {
        const state = get()
        const node = findNodeInTree(nodeId, state.treeStructure)
        if (!node) return null

        const clonedNode = deepCloneNode(node)

        // Find parent and add cloned node as sibling
        const parent = findParentNode(nodeId, state.treeStructure)

        set((s) => {
          if (parent && parent.children) {
            const index = parent.children.findIndex(c => c.id === nodeId)
            parent.children.splice(index + 1, 0, clonedNode)
          } else if (nodeId === 'root') {
            // Can't duplicate root - just select it
            return
          }
          s.selectedNodeId = clonedNode.id
          s.isDirty = true
        })

        get().autoSaveDraft()
        return clonedNode.id
      },

      // Reordering
      reorderNodes: (parentId: string, fromIndex: number, toIndex: number) => {
        set((state) => {
          const parent = findNodeInTree(parentId, state.treeStructure)
          if (parent && parent.children && parent.children.length > 0) {
            const [moved] = parent.children.splice(fromIndex, 1)
            parent.children.splice(toIndex, 0, moved)
            state.isDirty = true
          }
        })
        get().autoSaveDraft()
      },

      moveNode: (nodeId: string, targetParentId: string, targetIndex: number) => {
        set((state) => {
          // Find and remove from current parent
          const currentParent = findParentNode(nodeId, state.treeStructure)
          if (!currentParent?.children) return

          const sourceIndex = currentParent.children.findIndex(c => c.id === nodeId)
          if (sourceIndex === -1) return

          const [movedNode] = currentParent.children.splice(sourceIndex, 1)

          // Find target parent and insert
          const targetParent = findNodeInTree(targetParentId, state.treeStructure)
          if (!targetParent) return

          if (!targetParent.children) {
            targetParent.children = []
          }

          // Adjust index if moving within same parent and source was before target
          let adjustedIndex = targetIndex
          if (currentParent.id === targetParent.id && sourceIndex < targetIndex) {
            adjustedIndex = targetIndex - 1
          }

          targetParent.children.splice(adjustedIndex, 0, movedNode)
          state.isDirty = true
        })
        get().autoSaveDraft()
      },

      reorderOptions: (nodeId: string, fromIndex: number, toIndex: number) => {
        set((state) => {
          const node = findNodeInTree(nodeId, state.treeStructure)
          if (node && node.options && node.options.length > 0) {
            const [moved] = node.options.splice(fromIndex, 1)
            node.options.splice(toIndex, 0, moved)
            state.isDirty = true
          }
        })
        get().autoSaveDraft()
      },

      // Selection
      selectNode: (nodeId: string | null) => {
        set((state) => {
          state.selectedNodeId = nodeId
        })
      },

      // Validation
      validate: () => {
        const state = get()
        const errors: ValidationError[] = []

        // Check tree name
        if (!state.name.trim()) {
          errors.push({ message: 'Tree name is required', severity: 'error' })
        }

        // Check tree structure exists
        if (!state.treeStructure) {
          errors.push({ message: 'Tree must have at least one node', severity: 'error' })
          set((s) => { s.validationErrors = errors })
          return errors
        }

        const allNodeIds = getAllNodeIds(state.treeStructure)
        const referencedIds = new Set<string>()
        let hasSolution = false

        // Traverse and validate all nodes
        const validateNode = (node: TreeStructure) => {
          // Check type-specific required fields
          if (node.type === 'decision') {
            if (!node.question?.trim()) {
              errors.push({
                nodeId: node.id,
                field: 'question',
                message: `Decision node "${node.id}" requires a question`,
                severity: 'error'
              })
            }
            if (!node.options || node.options.length === 0) {
              errors.push({
                nodeId: node.id,
                field: 'options',
                message: `Decision node "${node.id}" requires at least one option`,
                severity: 'error'
              })
            }
            // Decision nodes with children must have at least 2 branches
            if (node.children && node.children.length > 0 && node.children.length < 2) {
              errors.push({
                nodeId: node.id,
                field: 'children',
                message: `Decision node "${node.id}" must have at least 2 branches`,
                severity: 'error'
              })
            }
            if (node.options && node.options.length > 0) {
              // Validate options
              node.options.forEach((opt, i) => {
                if (!opt.label?.trim()) {
                  errors.push({
                    nodeId: node.id,
                    field: `options[${i}].label`,
                    message: `Option ${i + 1} in "${node.id}" requires a label`,
                    severity: 'error'
                  })
                }
                if (opt.next_node_id) {
                  referencedIds.add(opt.next_node_id)
                  if (!allNodeIds.includes(opt.next_node_id)) {
                    errors.push({
                      nodeId: node.id,
                      field: `options[${i}].next_node_id`,
                      message: `Option "${opt.label}" references non-existent node "${opt.next_node_id}"`,
                      severity: 'error'
                    })
                  }
                }
              })
            }
          }

          if (node.type === 'action') {
            if (!node.title?.trim()) {
              errors.push({
                nodeId: node.id,
                field: 'title',
                message: `Action node "${node.id}" requires a title`,
                severity: 'error'
              })
            }
            if (node.next_node_id) {
              referencedIds.add(node.next_node_id)
              if (!allNodeIds.includes(node.next_node_id)) {
                errors.push({
                  nodeId: node.id,
                  field: 'next_node_id',
                  message: `Action "${node.title}" references non-existent node "${node.next_node_id}"`,
                  severity: 'error'
                })
              }
            }
          }

          if (node.type === 'solution') {
            hasSolution = true
            if (!node.title?.trim()) {
              errors.push({
                nodeId: node.id,
                field: 'title',
                message: `Solution node "${node.id}" requires a title`,
                severity: 'error'
              })
            }
          }

          // Validate children
          if (node.children) {
            node.children.forEach(child => validateNode(child))
          }
        }

        validateNode(state.treeStructure)

        // Check for circular references in next_node_id chains
        const detectCircularRefs = (startId: string, visited: Set<string> = new Set()): boolean => {
          if (visited.has(startId)) return true
          visited.add(startId)

          const node = findNodeInTree(startId, state.treeStructure)
          if (!node) return false

          // Check options
          if (node.options) {
            for (const opt of node.options) {
              if (opt.next_node_id && detectCircularRefs(opt.next_node_id, new Set(visited))) {
                errors.push({
                  nodeId: node.id,
                  message: `This path loops back to an earlier node via "${opt.label}"`,
                  severity: 'warning'
                })
                return true
              }
            }
          }

          // Check next_node_id
          if (node.next_node_id && detectCircularRefs(node.next_node_id, new Set(visited))) {
            errors.push({
              nodeId: node.id,
              message: `This node loops back to an earlier node ("${node.title || node.id}")`,
              severity: 'warning'
            })
            return true
          }

          return false
        }

        // Run from root
        detectCircularRefs('root')

        // Check for at least one solution
        if (!hasSolution) {
          errors.push({
            message: 'Tree must have at least one solution (terminal) node',
            severity: 'error'
          })
        }

        // Check for orphaned nodes (not root and not referenced)
        allNodeIds.forEach(id => {
          if (id !== 'root' && !referencedIds.has(id)) {
            // Check if it's a direct child of another node (via children array)
            let isChildOfAny = false
            const checkIfChild = (node: TreeStructure) => {
              if (node.children?.some(c => c.id === id)) {
                isChildOfAny = true
              }
              node.children?.forEach(checkIfChild)
            }
            checkIfChild(state.treeStructure!)

            if (!isChildOfAny) {
              errors.push({
                nodeId: id,
                message: `Node "${id}" is orphaned (not reachable from root)`,
                severity: 'warning'
              })
            }
          }
        })

        set((s) => { s.validationErrors = errors })
        return errors
      },

      clearValidation: () => {
        set((state) => { state.validationErrors = [] })
      },

      // Auto-save draft (debounced externally, called after each change)
      autoSaveDraft: () => {
        const state = get()
        const draft = {
          treeId: state.treeId,
          name: state.name,
          description: state.description,
          category: state.category,
          categoryId: state.categoryId,
          tags: state.tags,
          isPublic: state.isPublic,
          treeStructure: state.treeStructure,
          savedAt: new Date().toISOString()
        }
        safeSetItem(DRAFT_STORAGE_KEY, JSON.stringify(draft))
        set((s) => { s.draftSavedAt = new Date() })
      },

      markSaved: () => {
        safeRemoveItem(DRAFT_STORAGE_KEY)
        set((state) => {
          state.isDirty = false
          state.lastSavedAt = new Date()
          state.draftSavedAt = null
          state.hasDraft = false
        })
      },

      getTreeForSave: (): TreeCreate | TreeUpdate => {
        const state = get()
        return {
          name: state.name,
          description: state.description || undefined,
          category: state.category || undefined,
          category_id: state.categoryId || undefined,
          tags: state.tags.length > 0 ? state.tags : undefined,
          is_public: state.isPublic,
          tree_structure: state.treeStructure!
        }
      },

      // Code Mode actions
      setEditorMode: (mode: 'form' | 'code') => {
        const current = get()
        if (mode === current.editorMode) return

        if (mode === 'code') {
          // Form → Code: generate markdown from tree structure (synchronous)
          const { treeStructure, name, description, category, tags } = current
          if (isEmptyTree(treeStructure) && !name.trim()) {
            // New empty tree: use starter template
            set((state) => {
              state.markdownSource = CODE_MODE_STARTER_TEMPLATE
              state.markdownValidationErrors = []
              state.isMarkdownValid = false // needs validation
              state.isValidating = false
              state.editorMode = mode
            })
          } else if (treeStructure) {
            const metadata = { name, description, category, tags }
            const md = treeStructureToMarkdownPreview(treeStructure, metadata)
            set((state) => {
              state.markdownSource = md
              state.markdownValidationErrors = []
              state.isMarkdownValid = true
              state.isValidating = false
              state.editorMode = mode
            })
          } else {
            set((state) => { state.editorMode = mode })
          }
        } else {
          // Code → Form: apply last valid parse to tree structure
          get().syncMarkdownToTree()
          set((state) => { state.editorMode = mode })
        }
      },

      setMarkdownSource: (markdown: string) => {
        set((state) => {
          state.markdownSource = markdown
          state.isDirty = true
          state.isValidating = true
        })
      },

      setMarkdownValidationResult: (result: TreeMarkdownValidation) => {
        set((state) => {
          state.markdownValidationErrors = result.errors
          state.isMarkdownValid = result.valid
          state.isValidating = false
          if (result.valid && result.tree_structure) {
            state.lastValidTreeFromMarkdown = result.tree_structure as TreeStructure
            // Live sync: apply valid parsed tree to treeStructure immediately
            state.treeStructure = result.tree_structure as TreeStructure
          }
          // Apply metadata from parsed markdown (bidirectional sync)
          if (result.metadata) {
            if (result.metadata.name !== undefined) state.name = result.metadata.name
            if (result.metadata.description !== undefined) state.description = result.metadata.description
            if (result.metadata.category !== undefined) state.category = result.metadata.category
            if (result.metadata.tags !== undefined) state.tags = result.metadata.tags
          }
        })
      },

      syncMarkdownToTree: () => {
        const { lastValidTreeFromMarkdown, isMarkdownValid } = get()
        if (isMarkdownValid && lastValidTreeFromMarkdown) {
          set((state) => {
            state.treeStructure = lastValidTreeFromMarkdown
            state.markdownSource = null
            state.isDirty = true
          })
        }
      },

      syncTreeToMarkdown: () => {
        const { treeStructure, name, description, category, tags } = get()
        if (treeStructure) {
          const metadata = { name, description, category, tags }
          const md = treeStructureToMarkdownPreview(treeStructure, metadata)
          set((state) => {
            state.markdownSource = md
            state.markdownValidationErrors = []
            state.isMarkdownValid = true
            state.isValidating = false
          })
        }
      },

      setLoading: (loading: boolean) => {
        set((state) => { state.isLoading = loading })
      },

      setSaving: (saving: boolean) => {
        set((state) => { state.isSaving = saving })
      },

      // Helpers
      findNode: (nodeId: string) => {
        return findNodeInTree(nodeId, get().treeStructure)
      },

      getAllNodeIds: () => {
        return getAllNodeIds(get().treeStructure)
      },

      getAvailableTargetNodes: (excludeNodeId?: string) => {
        const state = get()
        const allIds = getAllNodeIds(state.treeStructure)

        return allIds
          .filter(id => id !== excludeNodeId)
          .map(id => {
            const node = findNodeInTree(id, state.treeStructure)
            let type: NodeType = 'decision'
            let title = ''

            if (node) {
              type = node.type
              if (node.question) title = node.question.slice(0, 50)
              else if (node.title) title = node.title.slice(0, 50)
            }

            // Use short ID format, but 'root' stays as-is
            const shortId = id === 'root' ? 'root' : id.slice(0, 8) + '...'

            // Format: "Title (shortId)" or just "(shortId)" if no title
            // For root without a question, show "Root Question (root)"
            let label: string
            if (id === 'root') {
              label = title ? `${title} (root)` : 'Root Question (root)'
            } else if (title) {
              label = `${title} (${shortId})`
            } else {
              // No title yet - show placeholder with ID
              const typeName = type === 'decision' ? 'Untitled Question' : `Untitled ${type}`
              label = `${typeName} (${shortId})`
            }

            return { id, label, type }
          })
      }
    })),
    {
      // Zundo options for undo/redo
      limit: 50, // Keep last 50 states
      partialize: (state) => ({
        // Only track these fields in history
        name: state.name,
        description: state.description,
        category: state.category,
        categoryId: state.categoryId,
        tags: state.tags,
        isPublic: state.isPublic,
        treeStructure: state.treeStructure
      }),
      // Skip no-op entries where partialized fields haven't changed
      equality: (pastState, currentState) => shallow(pastState, currentState),
      // Throttle history captures: collapse rapid changes (typing, validation) into ~one entry per 3s
      // This makes Flow Mode undo revert meaningful chunks (whole field edits) instead of single characters
      handleSet: (handleSet) =>
        throttle<typeof handleSet>((state) => {
          handleSet(state)
        }, 3000),
    }
  )
)

// Export temporal store for undo/redo access
// Use with: useStore(useTreeEditorStore.temporal, selector)
export const useTreeEditorTemporal = useTreeEditorStore.temporal

export default useTreeEditorStore
