import { create } from 'zustand'
import { temporal } from 'zundo'
import { immer } from 'zustand/middleware/immer'
import type { Tree, IntakeFormField, IntakeFieldType, ProceduralStep, ProceduralTreeStructure, TreeType } from '@/types'

export interface ProceduralValidationError {
  stepId?: string
  field?: string
  message: string
  severity: 'error' | 'warning'
}

const generateId = () => crypto.randomUUID()

const FIELD_TYPE_PREFIX: Record<IntakeFieldType, string> = {
  text: 'text',
  textarea: 'textarea',
  number: 'number',
  ip_address: 'ip',
  email: 'email',
  url: 'url',
  select: 'select',
  multi_select: 'multiselect',
  checkbox: 'checkbox',
  password: 'password',
}

function generateVariableName(fieldType: IntakeFieldType, existingFields: IntakeFormField[]): string {
  const prefix = FIELD_TYPE_PREFIX[fieldType] || 'field'
  const count = existingFields.filter((f) => f.field_type === fieldType).length
  return `${prefix}_${count + 1}`
}

function createDefaultStep(index: number): ProceduralStep {
  return {
    id: generateId(),
    type: 'procedure_step',
    title: `Step ${index + 1}`,
    description: '',
    content_type: 'action',
    notes_enabled: true,
  }
}

function createEndStep(): ProceduralStep {
  return {
    id: generateId(),
    type: 'procedure_end',
    title: 'Procedure Complete',
  }
}

function createSectionHeader(title: string): ProceduralStep {
  return {
    id: generateId(),
    type: 'section_header',
    title,
  }
}

function createDefaultField(index: number, existingFields: IntakeFormField[]): IntakeFormField {
  return {
    variable_name: generateVariableName('text', existingFields),
    label: `Field ${index + 1}`,
    field_type: 'text',
    required: true,
    display_order: index + 1,
  }
}

interface ProceduralEditorState {
  // Tree metadata
  treeId: string | null
  treeType: TreeType
  name: string
  description: string
  categoryId: string | null
  tags: string[]
  isPublic: boolean
  status: 'draft' | 'published'

  // Procedural data
  steps: ProceduralStep[]
  intakeForm: IntakeFormField[]

  // UI state
  selectedStepId: string | null
  expandedStepId: string | null
  isDirty: boolean
  isLoading: boolean
  isSaving: boolean
  validationErrors: ProceduralValidationError[]

  // Actions - Init
  initNew: (type?: TreeType) => void
  loadTree: (tree: Tree) => void
  reset: () => void

  // Actions - Metadata
  setTreeType: (treeType: TreeType) => void
  setName: (name: string) => void
  setDescription: (description: string) => void
  setCategoryId: (categoryId: string | null) => void
  setTags: (tags: string[]) => void
  setIsPublic: (isPublic: boolean) => void
  setStatus: (status: 'draft' | 'published') => void

  // Actions - Steps
  addStep: (afterIndex?: number) => void
  addSectionHeader: (afterIndex?: number) => void
  removeStep: (stepId: string) => void
  updateStep: (stepId: string, updates: Partial<ProceduralStep>) => void
  moveStep: (fromIndex: number, toIndex: number) => void
  setSelectedStepId: (stepId: string | null) => void
  setExpandedStepId: (stepId: string | null) => void

  // Actions - Intake Form
  addField: () => void
  removeField: (index: number) => void
  updateField: (index: number, updates: Partial<IntakeFormField>) => void
  moveField: (fromIndex: number, toIndex: number) => void

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

  // Actions - AI Integration
  replaceSteps: (steps: ProceduralStep[], intakeForm?: IntakeFormField[]) => void

  // Actions - Save
  setIsSaving: (saving: boolean) => void
  markSaved: () => void
  getTreeForSave: () => {
    name: string
    description: string
    tree_type: TreeType
    tree_structure: ProceduralTreeStructure
    intake_form: IntakeFormField[] | undefined
    category_id: string | null
    tags: string[]
    is_public: boolean
    status: 'draft' | 'published'
  }
}

export const useProceduralEditorStore = create<ProceduralEditorState>()(
  temporal(
    immer((set, get) => ({
      // Initial state
      treeId: null,
      treeType: 'procedural' as TreeType,
      name: '',
      description: '',
      categoryId: null,
      tags: [],
      isPublic: false,
      status: 'draft' as const,
      steps: [],
      intakeForm: [],
      selectedStepId: null,
      expandedStepId: null,
      isDirty: false,
      isLoading: false,
      isSaving: false,
      validationErrors: [],

      // Init
      initNew: (type?: TreeType) => {
        set((state) => {
          state.treeId = null
          state.treeType = type || 'procedural'
          state.name = ''
          state.description = ''
          state.categoryId = null
          state.tags = []
          state.isPublic = false
          state.status = 'draft'
          state.steps = [createDefaultStep(0), createEndStep()]
          state.intakeForm = []
          state.selectedStepId = null
          state.expandedStepId = null
          state.isDirty = false
          state.isLoading = false
          state.isSaving = false
        })
      },

      loadTree: (tree: Tree) => {
        const structure = tree.tree_structure as unknown as ProceduralTreeStructure
        set((state) => {
          state.treeId = tree.id
          state.treeType = tree.tree_type
          state.name = tree.name
          state.description = tree.description || ''
          state.categoryId = tree.category_id
          state.tags = tree.tags || []
          state.isPublic = tree.is_public
          state.status = tree.status
          state.steps = structure.steps || [createDefaultStep(0), createEndStep()]
          state.intakeForm = tree.intake_form || []
          state.selectedStepId = null
          state.expandedStepId = null
          state.isDirty = false
          state.isLoading = false
          state.isSaving = false
        })
      },

      reset: () => {
        set((state) => {
          state.treeId = null
          state.treeType = 'procedural'
          state.name = ''
          state.description = ''
          state.categoryId = null
          state.tags = []
          state.isPublic = false
          state.status = 'draft'
          state.steps = []
          state.intakeForm = []
          state.selectedStepId = null
          state.expandedStepId = null
          state.isDirty = false
          state.isLoading = false
          state.isSaving = false
        })
      },

      // Metadata
      setTreeType: (treeType) => set((state) => { state.treeType = treeType }),
      setName: (name) => set((state) => { state.name = name; state.isDirty = true }),
      setDescription: (description) => set((state) => { state.description = description; state.isDirty = true }),
      setCategoryId: (categoryId) => set((state) => { state.categoryId = categoryId; state.isDirty = true }),
      setTags: (tags) => set((state) => { state.tags = tags; state.isDirty = true }),
      setIsPublic: (isPublic) => set((state) => { state.isPublic = isPublic; state.isDirty = true }),
      setStatus: (status) => set((state) => { state.status = status; state.isDirty = true }),

      // Steps
      addStep: (afterIndex) => {
        set((state) => {
          // Find the insert position (before the end step)
          const endIndex = state.steps.findIndex((s) => s.type === 'procedure_end')
          const insertAt = afterIndex !== undefined
            ? Math.min(afterIndex + 1, endIndex >= 0 ? endIndex : state.steps.length)
            : (endIndex >= 0 ? endIndex : state.steps.length)

          const newStep = createDefaultStep(insertAt)
          state.steps.splice(insertAt, 0, newStep)
          state.expandedStepId = newStep.id
          state.isDirty = true
        })
      },

      addSectionHeader: (afterIndex) => {
        set((state) => {
          const endIndex = state.steps.findIndex((s) => s.type === 'procedure_end')
          const insertAt = afterIndex !== undefined
            ? Math.min(afterIndex + 1, endIndex >= 0 ? endIndex : state.steps.length)
            : (endIndex >= 0 ? endIndex : state.steps.length)

          const newHeader = createSectionHeader('New Section')
          state.steps.splice(insertAt, 0, newHeader)
          state.expandedStepId = newHeader.id
          state.isDirty = true
        })
      },

      removeStep: (stepId) => {
        set((state) => {
          const index = state.steps.findIndex((s) => s.id === stepId)
          if (index === -1) return
          // Don't remove the end step
          if (state.steps[index].type === 'procedure_end') return
          // Don't remove if it's the only procedure_step (section headers can always be removed)
          if (state.steps[index].type === 'procedure_step') {
            const stepCount = state.steps.filter((s) => s.type === 'procedure_step').length
            if (stepCount <= 1) return
          }

          state.steps.splice(index, 1)
          if (state.selectedStepId === stepId) state.selectedStepId = null
          if (state.expandedStepId === stepId) state.expandedStepId = null
          state.isDirty = true
        })
      },

      updateStep: (stepId, updates) => {
        set((state) => {
          const step = state.steps.find((s) => s.id === stepId)
          if (step) {
            Object.assign(step, updates)
            state.isDirty = true
          }
        })
      },

      moveStep: (fromIndex, toIndex) => {
        set((state) => {
          // Don't move the end step
          if (state.steps[fromIndex]?.type === 'procedure_end') return
          // Don't move past the end step
          const endIndex = state.steps.findIndex((s) => s.type === 'procedure_end')
          if (toIndex >= endIndex) return

          const [moved] = state.steps.splice(fromIndex, 1)
          state.steps.splice(toIndex, 0, moved)
          state.isDirty = true
        })
      },

      setSelectedStepId: (stepId) => set((state) => { state.selectedStepId = stepId }),
      setExpandedStepId: (stepId) => set((state) => {
        state.expandedStepId = state.expandedStepId === stepId ? null : stepId
      }),

      // Intake Form
      addField: () => {
        set((state) => {
          const newField = createDefaultField(state.intakeForm.length, state.intakeForm)
          state.intakeForm.push(newField)
          state.isDirty = true
        })
      },

      removeField: (index) => {
        set((state) => {
          if (index < 0 || index >= state.intakeForm.length) return
          state.intakeForm.splice(index, 1)
          // Reorder display_order
          state.intakeForm.forEach((f, i) => { f.display_order = i + 1 })
          state.isDirty = true
        })
      },

      updateField: (index, updates) => {
        set((state) => {
          const field = state.intakeForm[index]
          if (field) {
            // If field_type changed, auto-generate a new variable name
            if (updates.field_type && updates.field_type !== field.field_type) {
              const otherFields = state.intakeForm.filter((_, i) => i !== index)
              updates.variable_name = generateVariableName(updates.field_type, otherFields)
            }
            Object.assign(field, updates)
            state.isDirty = true
          }
        })
      },

      moveField: (fromIndex, toIndex) => {
        set((state) => {
          const [moved] = state.intakeForm.splice(fromIndex, 1)
          state.intakeForm.splice(toIndex, 0, moved)
          // Reorder display_order
          state.intakeForm.forEach((f, i) => { f.display_order = i + 1 })
          state.isDirty = true
        })
      },

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

        // Rule 1: Name required
        if (!state.name || state.name.trim() === '') {
          errors.push({ message: 'Flow name is required', severity: 'error' })
        }

        // Exclude ghost/suggestion steps from validation
        const realSteps = state.steps.filter((s: ProceduralStep) => !(s as ProceduralStep & { _suggestion?: boolean })._suggestion)

        // Rule 2: Must have at least one step
        if (realSteps.length === 0) {
          errors.push({ message: 'Flow must have at least one step', severity: 'error' })
        } else {
          // Rule 3: Must have exactly one procedure_end step, and it must be last.
          // NOTE: procedure_end errors intentionally omit stepId so they render as
          // non-navigable in ValidationSummary (there is no data-step-id on the end step).
          const endSteps = realSteps.filter((s: ProceduralStep) => s.type === 'procedure_end')
          if (endSteps.length === 0) {
            errors.push({ message: 'Flow must have a completion step at the end', severity: 'error' })
          } else if (endSteps.length > 1) {
            errors.push({
              message: `Flow has ${endSteps.length} completion steps — only one is allowed`,
              severity: 'error',
            })
          } else if (realSteps[realSteps.length - 1]?.type !== 'procedure_end') {
            errors.push({
              message: 'Completion step must be the last step',
              severity: 'error',
            })
          }

          // Rule 4: Each procedure_step must have a non-empty title
          realSteps
            .filter((s: ProceduralStep) => s.type === 'procedure_step')
            .forEach((s: ProceduralStep) => {
              if (!s.title || s.title.trim() === '') {
                errors.push({
                  stepId: s.id,
                  field: 'title',
                  message: 'Step is missing a title',
                  severity: 'error',
                })
              }
            })

          // Rule 5: No duplicate step IDs
          const seen = new Set<string>()
          realSteps.forEach((s: ProceduralStep) => {
            if (seen.has(s.id)) {
              errors.push({ stepId: s.id, message: `Duplicate step ID: ${s.id}`, severity: 'error' })
            }
            seen.add(s.id)
          })

          // Rule 6: section_header with no procedure_steps following it (warning)
          realSteps.forEach((s: ProceduralStep, idx: number) => {
            if (s.type === 'section_header') {
              const next = realSteps[idx + 1]
              if (!next || next.type === 'section_header' || next.type === 'procedure_end') {
                errors.push({
                  stepId: s.id,
                  message: 'Section header has no steps beneath it',
                  severity: 'warning',
                })
              }
            }
          })
        }

        // Rule 7: Intake form validation
        if (state.intakeForm && state.intakeForm.length > 0) {
          const varNames = new Set<string>()
          state.intakeForm.forEach((field: IntakeFormField) => {
            if (!field.variable_name || field.variable_name.trim() === '') {
              errors.push({
                field: 'intake_form',
                message: `Intake field "${field.label || 'unnamed'}" is missing a variable name`,
                severity: 'error',
              })
            } else {
              if (varNames.has(field.variable_name)) {
                errors.push({
                  field: 'intake_form',
                  message: `Duplicate intake field variable name: ${field.variable_name}`,
                  severity: 'error',
                })
              }
              varNames.add(field.variable_name)
            }
            if (
              (field.field_type === 'select' || field.field_type === 'multi_select') &&
              (!field.options || field.options.length === 0)
            ) {
              errors.push({
                field: 'intake_form',
                message: `Intake field "${field.label}" needs at least one option`,
                severity: 'error',
              })
            }
          })
        }

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

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

      // AI Integration
      replaceSteps: (steps, intakeForm) => {
        set((state) => {
          state.steps = steps
          if (intakeForm) state.intakeForm = intakeForm
          state.isDirty = true
        })
      },

      // Save
      setIsSaving: (saving) => set((state) => { state.isSaving = saving }),
      markSaved: () => set((state) => { state.isDirty = false }),

      getTreeForSave: () => {
        const state = get()
        return {
          name: state.name,
          description: state.description,
          tree_type: state.treeType,
          tree_structure: { steps: state.steps },
          intake_form: state.intakeForm.length > 0 ? state.intakeForm : undefined,
          category_id: state.categoryId,
          tags: state.tags,
          is_public: state.isPublic,
          status: state.status,
        }
      },
    })),
    { limit: 50 }
  )
)

export default useProceduralEditorStore
