'use client'

import { useState, useCallback, useRef, useMemo } from 'react'
import type {
  PageNode,
  BuilderNode,
  BlockNode,
  SectionNode,
  ColumnNode,
} from '@actuate-media/cms-core'
import {
  addNode,
  removeNode,
  moveNode,
  updateNodeSettings,
  updateBlockData,
  findNode,
  findParent,
  createEmptyPage,
  createSection,
  createContainer,
  createRow,
  createColumn,
  createBlock,
  hasChildren,
} from '@actuate-media/cms-core'

export type DeviceMode = 'desktop' | 'tablet' | 'mobile'
export type PanelTab = 'block' | 'node' | 'page' | 'seo' | 'design'

export interface PageSettings {
  title: string
  slug: string
  template?: string
  metaTitle?: string
  metaDescription?: string
  ogImage?: string
  focusKeyphrase?: string
  schemaType?: string
}

export interface BuilderState {
  tree: PageNode
  selectedNodeId: string | null
  selectedNode: BuilderNode | null
  deviceMode: DeviceMode
  activeTab: PanelTab
  pageSettings: PageSettings
  dirty: boolean
  canUndo: boolean
  canRedo: boolean
  showGridOverlay: boolean
}

export interface BuilderActions {
  selectNode: (id: string | null) => void
  setDeviceMode: (mode: DeviceMode) => void
  setActiveTab: (tab: PanelTab) => void
  setPageSettings: (settings: Partial<PageSettings>) => void
  setShowGridOverlay: (show: boolean) => void

  addSection: () => void
  addRowToSection: (sectionId: string) => void
  addBlockToColumn: (columnId: string, blockType: string, variant?: string) => void
  addNodeAtId: (parentId: string, node: BuilderNode, index?: number) => void
  removeNodeById: (id: string) => void
  moveNodeById: (id: string, newParentId: string, index?: number) => void
  updateSettings: (id: string, settings: Record<string, unknown>) => void
  updateBlock: (id: string, data: Record<string, unknown>) => void
  duplicateNode: (id: string) => void
  moveNodeUp: (id: string) => void
  moveNodeDown: (id: string) => void

  undo: () => void
  redo: () => void
  markClean: () => void
  replaceTree: (tree: PageNode) => void
}

const MAX_HISTORY = 50

function deepClone<T>(obj: T): T {
  return JSON.parse(JSON.stringify(obj))
}

function regenerateIds(node: BuilderNode): BuilderNode {
  const cloned = deepClone(node)
  let counter = 0
  function walk(n: any) {
    n.id = `node_${Date.now().toString(36)}_${(counter++).toString(36)}_${Math.random().toString(36).slice(2, 8)}`
    if (Array.isArray(n.children)) {
      for (const child of n.children) walk(child)
    }
  }
  walk(cloned)
  return cloned
}

export function useBuilderState(
  initialTree?: PageNode,
  initialPageSettings?: Partial<PageSettings>,
): BuilderState & BuilderActions {
  const [tree, setTree] = useState<PageNode>(() => initialTree ?? createEmptyPage())
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null)
  const [deviceMode, setDeviceMode] = useState<DeviceMode>('desktop')
  const [activeTab, setActiveTab] = useState<PanelTab>('page')
  const [showGridOverlay, setShowGridOverlay] = useState(false)
  const [pageSettings, setPageSettingsState] = useState<PageSettings>({
    title: '',
    slug: '',
    ...initialPageSettings,
  })
  const [dirty, setDirty] = useState(false)

  const undoStack = useRef<PageNode[]>([])
  const redoStack = useRef<PageNode[]>([])

  const pushHistory = useCallback((currentTree: PageNode) => {
    undoStack.current = [...undoStack.current.slice(-MAX_HISTORY + 1), currentTree]
    redoStack.current = []
  }, [])

  const [canUndo, setCanUndo] = useState(false)
  const [canRedo, setCanRedo] = useState(false)

  const applyTreeChange = useCallback(
    (fn: (prev: PageNode) => PageNode) => {
      setTree((prev) => {
        pushHistory(prev)
        const next = fn(prev)
        setDirty(true)
        setCanUndo(true)
        setCanRedo(false)
        return next
      })
    },
    [pushHistory],
  )

  const selectedNode = useMemo(() => {
    if (!selectedNodeId) return null
    return findNode(tree, selectedNodeId) ?? null
  }, [tree, selectedNodeId])

  const selectNode = useCallback(
    (id: string | null) => {
      setSelectedNodeId(id)
      if (id) {
        const node = findNode(tree, id)
        if (node) {
          if (node.type === 'block') setActiveTab('block')
          else if (node.type === 'page') setActiveTab('page')
          else setActiveTab('node')
        }
      }
    },
    [tree],
  )

  const setPageSettings = useCallback((settings: Partial<PageSettings>) => {
    setPageSettingsState((prev) => ({ ...prev, ...settings }))
    setDirty(true)
  }, [])

  const addSection = useCallback(() => {
    applyTreeChange((prev) => {
      const section = createSection({ paddingTop: '64px', paddingBottom: '64px' })
      const container = createContainer()
      const col = createColumn(12)
      const row = createRow([col])
      ;(container as any).children = [row]
      ;(section as any).children = [container]
      return addNode(prev, prev.id, section as BuilderNode)
    })
  }, [applyTreeChange])

  const addRowToSection = useCallback(
    (sectionId: string) => {
      applyTreeChange((prev) => {
        const section = findNode(prev, sectionId)
        if (!section || !hasChildren(section)) return prev
        const containers = (section as SectionNode).children.filter((c) => c.type === 'container')
        const targetId = containers.length > 0 ? containers[containers.length - 1]!.id : sectionId
        const col = createColumn(12)
        const row = createRow([col])
        return addNode(prev, targetId, row as BuilderNode)
      })
    },
    [applyTreeChange],
  )

  const addBlockToColumn = useCallback(
    (columnId: string, blockType: string, variant?: string) => {
      applyTreeChange((prev) => {
        const block = createBlock(blockType, variant)
        return addNode(prev, columnId, block as BuilderNode)
      })
    },
    [applyTreeChange],
  )

  const addNodeAtId = useCallback(
    (parentId: string, node: BuilderNode, index?: number) => {
      applyTreeChange((prev) => addNode(prev, parentId, node, index))
    },
    [applyTreeChange],
  )

  const removeNodeById = useCallback(
    (id: string) => {
      applyTreeChange((prev) => {
        const result = removeNode(prev, id)
        if (selectedNodeId === id) setSelectedNodeId(null)
        return result
      })
    },
    [applyTreeChange, selectedNodeId],
  )

  const moveNodeById = useCallback(
    (id: string, newParentId: string, index?: number) => {
      applyTreeChange((prev) => moveNode(prev, id, newParentId, index))
    },
    [applyTreeChange],
  )

  const updateSettings = useCallback(
    (id: string, settings: Record<string, unknown>) => {
      applyTreeChange((prev) => updateNodeSettings(prev, id, settings))
    },
    [applyTreeChange],
  )

  const updateBlock = useCallback(
    (id: string, data: Record<string, unknown>) => {
      applyTreeChange((prev) => updateBlockData(prev, id, data))
    },
    [applyTreeChange],
  )

  const duplicateNode = useCallback(
    (id: string) => {
      applyTreeChange((prev) => {
        const node = findNode(prev, id)
        if (!node) return prev
        const parent = findParent(prev, id)
        if (!parent) return prev
        const siblings = (parent as any).children as BuilderNode[]
        const idx = siblings.findIndex((c) => c.id === id)
        const cloned = regenerateIds(node)
        return addNode(prev, parent.id, cloned, idx + 1)
      })
    },
    [applyTreeChange],
  )

  const moveNodeUp = useCallback(
    (id: string) => {
      applyTreeChange((prev) => {
        const parent = findParent(prev, id)
        if (!parent) return prev
        const siblings = (parent as any).children as BuilderNode[]
        const idx = siblings.findIndex((c) => c.id === id)
        if (idx <= 0) return prev
        const withoutNode = removeNode(prev, id)
        const node = findNode(prev, id)
        if (!node) return prev
        return addNode(withoutNode, parent.id, deepClone(node), idx - 1)
      })
    },
    [applyTreeChange],
  )

  const moveNodeDown = useCallback(
    (id: string) => {
      applyTreeChange((prev) => {
        const parent = findParent(prev, id)
        if (!parent) return prev
        const siblings = (parent as any).children as BuilderNode[]
        const idx = siblings.findIndex((c) => c.id === id)
        if (idx >= siblings.length - 1) return prev
        const withoutNode = removeNode(prev, id)
        const node = findNode(prev, id)
        if (!node) return prev
        return addNode(withoutNode, parent.id, deepClone(node), idx + 1)
      })
    },
    [applyTreeChange],
  )

  const undo = useCallback(() => {
    if (undoStack.current.length === 0) return
    const prev = undoStack.current[undoStack.current.length - 1]!
    undoStack.current = undoStack.current.slice(0, -1)
    setTree((current) => {
      redoStack.current = [...redoStack.current, current]
      setCanUndo(undoStack.current.length > 0)
      setCanRedo(true)
      return prev
    })
  }, [])

  const redo = useCallback(() => {
    if (redoStack.current.length === 0) return
    const next = redoStack.current[redoStack.current.length - 1]!
    redoStack.current = redoStack.current.slice(0, -1)
    setTree((current) => {
      undoStack.current = [...undoStack.current, current]
      setCanRedo(redoStack.current.length > 0)
      setCanUndo(true)
      return next
    })
  }, [])

  const markClean = useCallback(() => {
    setDirty(false)
  }, [])

  const replaceTree = useCallback(
    (newTree: PageNode) => {
      setTree((prev) => {
        pushHistory(prev)
        setDirty(true)
        setCanUndo(true)
        setCanRedo(false)
        return newTree
      })
    },
    [pushHistory],
  )

  return {
    tree,
    selectedNodeId,
    selectedNode,
    deviceMode,
    activeTab,
    pageSettings,
    dirty,
    canUndo,
    canRedo,
    showGridOverlay,

    selectNode,
    setDeviceMode,
    setActiveTab,
    setPageSettings,
    setShowGridOverlay,

    addSection,
    addRowToSection,
    addBlockToColumn,
    addNodeAtId,
    removeNodeById,
    moveNodeById,
    updateSettings,
    updateBlock,
    duplicateNode,
    moveNodeUp,
    moveNodeDown,

    undo,
    redo,
    markClean,
    replaceTree,
  }
}
