import { useEffect } from 'react'
import { useCanvasStore } from '@/stores/canvas-store'
import { useDocumentStore, getActivePageChildren } from '@/stores/document-store'
import { useHistoryStore } from '@/stores/history-store'
import { cloneNodesWithNewIds } from '@/utils/node-clone'
import { canBooleanOp, executeBooleanOp, type BooleanOpType } from '@/utils/boolean-ops'
import { tryPasteFigmaFromClipboard } from '@/hooks/use-figma-paste'
import {
  supportsFileSystemAccess,
  isElectron,
  writeToFileHandle,
  writeToFilePath,
  saveDocumentAs,
  downloadDocument,
  openDocumentFS,
  openDocument,
} from '@/utils/file-operations'
import { syncCanvasPositionsToStore, zoomToFitContent } from '@/canvas/skia-engine-ref'
import type { ToolType } from '@/types/canvas'

const TOOL_KEYS: Record<string, ToolType> = {
  v: 'select',
  f: 'frame',
  r: 'rectangle',
  o: 'ellipse',
  y: 'polygon',
  l: 'line',
  t: 'text',
  p: 'path',
  h: 'hand',
}

export function useKeyboardShortcuts() {
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Skip if user is typing in an input
      const target = e.target as HTMLElement
      if (
        target.tagName === 'INPUT' ||
        target.tagName === 'TEXTAREA' ||
        target.isContentEditable
      ) {
        return
      }

      const isMod = e.metaKey || e.ctrlKey

      // Undo: Cmd/Ctrl+Z
      if (isMod && e.key === 'z' && !e.shiftKey) {
        e.preventDefault()
        const currentDoc = useDocumentStore.getState().document
        const prev = useHistoryStore.getState().undo(currentDoc)
        if (prev) {
          useDocumentStore.getState().applyHistoryState(prev)
        }
        useCanvasStore.getState().clearSelection()
        return
      }

      // Redo: Cmd/Ctrl+Shift+Z
      if (isMod && e.key === 'z' && e.shiftKey) {
        e.preventDefault()
        const currentDoc = useDocumentStore.getState().document
        const next = useHistoryStore.getState().redo(currentDoc)
        if (next) {
          useDocumentStore.getState().applyHistoryState(next)
        }
        useCanvasStore.getState().clearSelection()
        return
      }

      // Copy: Cmd/Ctrl+C
      if (isMod && e.key === 'c' && !e.shiftKey) {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 0) {
          e.preventDefault()
          const nodes = selectedIds
            .map((id) => useDocumentStore.getState().getNodeById(id))
            .filter((n): n is NonNullable<typeof n> => n != null)
          useCanvasStore.getState().setClipboard(structuredClone(nodes))
        }
        return
      }

      // Cut: Cmd/Ctrl+X
      if (isMod && e.key === 'x' && !e.shiftKey) {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 0) {
          e.preventDefault()
          const nodes = selectedIds
            .map((id) => useDocumentStore.getState().getNodeById(id))
            .filter((n): n is NonNullable<typeof n> => n != null)
          useCanvasStore.getState().setClipboard(structuredClone(nodes))
          for (const id of selectedIds) {
            useDocumentStore.getState().removeNode(id)
          }
          useCanvasStore.getState().clearSelection()
        }
        return
      }

      // Paste: Cmd/Ctrl+V
      if (isMod && e.key === 'v' && !e.shiftKey) {
        const { clipboard } = useCanvasStore.getState()
        if (clipboard.length > 0) {
          e.preventDefault()
          const newIds: string[] = []
          for (const original of clipboard) {
            // Pasting a reusable component creates an instance (RefNode)
            if ('reusable' in original && original.reusable) {
              const component = useDocumentStore.getState().getNodeById(original.id)
              if (component && 'reusable' in component && component.reusable) {
                const newId = useDocumentStore.getState().duplicateNode(original.id)
                if (newId) {
                  newIds.push(newId)
                  continue
                }
              }
            }
            // Regular paste for non-reusable nodes
            const [cloned] = cloneNodesWithNewIds([original], 10)
            useDocumentStore.getState().addNode(null, cloned)
            newIds.push(cloned.id)
          }
          useCanvasStore.getState().setSelection(newIds, newIds[0] ?? null)
        } else {
          // Internal clipboard empty — try reading Figma data from system clipboard.
          // The native `paste` event may not fire when a non-editable element (canvas)
          // has focus, so we also read via the Clipboard API as a fallback.
          e.preventDefault()
          tryPasteFigmaFromClipboard()
        }
        return
      }

      // Duplicate: Cmd/Ctrl+D
      if (isMod && e.key === 'd') {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 0) {
          e.preventDefault()
          const newIds: string[] = []
          for (const id of selectedIds) {
            const newId = useDocumentStore.getState().duplicateNode(id)
            if (newId) newIds.push(newId)
          }
          if (newIds.length > 0) {
            useCanvasStore.getState().setSelection(newIds, newIds[0])
          }
        }
        return
      }

      // Save: Cmd/Ctrl+S (also Cmd/Ctrl+Shift+S)
      // If current file is .op with handle/path → save in-place
      // Otherwise → save as .op
      if (isMod && e.key === 's') {
        e.preventDefault()
        try { syncCanvasPositionsToStore() } catch { /* continue */ }
        const store = useDocumentStore.getState()
        const { document: doc, fileName, fileHandle, filePath } = store
        const isOpFile = fileName ? /\.op$/i.test(fileName) : false
        const suggestedName = fileName
          ? fileName.replace(/\.(pen|op|json)$/i, '') + '.op'
          : 'untitled.op'

        const doSave = async () => {
          // Electron with known .op path
          if (isElectron() && filePath && isOpFile) {
            await writeToFilePath(filePath, doc)
            store.markClean()
            return
          }
          // Browser with valid .op file handle
          if (fileHandle && isOpFile) {
            try {
              await writeToFileHandle(fileHandle, doc)
              store.markClean()
              return
            } catch {
              useDocumentStore.setState({ fileHandle: null })
            }
          }
          // Save as .op
          if (isElectron()) {
            const savedPath = await window.electronAPI!.saveFile(
              JSON.stringify(doc), suggestedName,
            )
            if (savedPath) {
              useDocumentStore.setState({
                fileName: savedPath.split(/[/\\]/).pop() || suggestedName,
                filePath: savedPath, fileHandle: null, isDirty: false,
              })
            }
          } else if (supportsFileSystemAccess()) {
            const result = await saveDocumentAs(doc, suggestedName)
            if (result) {
              useDocumentStore.setState({
                fileName: result.fileName, fileHandle: result.handle, isDirty: false,
              })
            }
          } else {
            downloadDocument(doc, suggestedName)
            store.markClean()
          }
        }
        doSave().catch((err) => console.error('[Save] Failed:', err))
        return
      }

      // Open: Cmd/Ctrl+O
      if (isMod && e.key === 'o' && !e.shiftKey) {
        e.preventDefault()
        if (supportsFileSystemAccess()) {
          openDocumentFS().then((result) => {
            if (result) {
              useDocumentStore
                .getState()
                .loadDocument(result.doc, result.fileName, result.handle)
              requestAnimationFrame(() => zoomToFitContent())
            }
          })
        } else {
          openDocument().then((result) => {
            if (result) {
              useDocumentStore
                .getState()
                .loadDocument(result.doc, result.fileName)
              requestAnimationFrame(() => zoomToFitContent())
            }
          })
        }
        return
      }

      // Group: Cmd/Ctrl+G
      if (isMod && e.key === 'g' && !e.shiftKey) {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length >= 2) {
          e.preventDefault()
          const groupId = useDocumentStore.getState().groupNodes(selectedIds)
          if (groupId) {
            useCanvasStore.getState().setSelection([groupId], groupId)
          }
        }
        return
      }

      // Create Component: Cmd/Ctrl+Alt+K
      if (isMod && e.altKey && e.key.toLowerCase() === 'k') {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length === 1) {
          e.preventDefault()
          useDocumentStore.getState().makeReusable(selectedIds[0])
        }
        return
      }

      // Ungroup: Cmd/Ctrl+Shift+G
      if (isMod && e.shiftKey && e.key.toLowerCase() === 'g') {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length === 1) {
          e.preventDefault()
          const node = useDocumentStore.getState().getNodeById(selectedIds[0])
          if (node && node.type === 'group' && 'children' in node && node.children) {
            const childIds = node.children.map((c) => c.id)
            useDocumentStore.getState().ungroupNode(selectedIds[0])
            useCanvasStore.getState().setSelection(childIds, childIds[0] ?? null)
          }
        }
        return
      }

      // Boolean operations: Cmd/Ctrl+Alt+U (union), Cmd/Ctrl+Alt+S (subtract), Cmd/Ctrl+Alt+I (intersect)
      if (isMod && e.altKey && !e.shiftKey) {
        const booleanOps: Record<string, BooleanOpType> = {
          u: 'union',
          s: 'subtract',
          i: 'intersect',
        }
        const opType = booleanOps[e.key.toLowerCase()]
        if (opType) {
          const { selectedIds } = useCanvasStore.getState().selection
          const nodes = selectedIds
            .map((id) => useDocumentStore.getState().getNodeById(id))
            .filter((n): n is NonNullable<typeof n> => n != null)
          if (canBooleanOp(nodes)) {
            e.preventDefault()
            const result = executeBooleanOp(nodes, opType)
            if (result) {
              useHistoryStore.getState().pushState(useDocumentStore.getState().document)
              for (const id of selectedIds) {
                useDocumentStore.getState().removeNode(id)
              }
              useDocumentStore.getState().addNode(null, result)
              useCanvasStore.getState().setSelection([result.id], result.id)
            }
          }
          return
        }
      }

      // Tool shortcuts (single key, no modifier)
      if (!isMod && !e.shiftKey && !e.altKey) {
        const tool = TOOL_KEYS[e.key.toLowerCase()]
        if (tool) {
          e.preventDefault()
          useCanvasStore.getState().setActiveTool(tool)
          return
        }
      }

      // Escape: 1) clear selection, 2) exit frame, 3) switch to select tool
      if (e.key === 'Escape') {
        e.preventDefault()
        const { selectedIds, enteredFrameId } = useCanvasStore.getState().selection

        if (selectedIds.length > 0) {
          useCanvasStore.getState().clearSelection()
        } else if (enteredFrameId) {
          useCanvasStore.getState().exitFrame()
        } else {
          useCanvasStore.getState().setActiveTool('select')
        }
        return
      }

      // Delete / Backspace: remove selected elements
      if (e.key === 'Delete' || e.key === 'Backspace') {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 0) {
          e.preventDefault()
          if (selectedIds.length > 1) {
            useHistoryStore
              .getState()
              .beginBatch(
                useDocumentStore.getState().document.children,
              )
          }
          for (const id of selectedIds) {
            useDocumentStore.getState().removeNode(id)
          }
          if (selectedIds.length > 1) {
            useHistoryStore
              .getState()
              .endBatch(useDocumentStore.getState().document)
          }
          useCanvasStore.getState().clearSelection()
        }
        return
      }

      // Cmd+A: select all (top-level nodes only, matching manual selection behavior)
      if (isMod && e.key === 'a') {
        e.preventDefault()
        const topLevelNodes = getActivePageChildren(useDocumentStore.getState().document, useCanvasStore.getState().activePageId)
        const ids = topLevelNodes.map((n) => n.id)
        useCanvasStore.getState().setSelection(ids, ids[0] ?? null)
        return
      }

      // [ ] : reorder layers
      if (e.key === '[') {
        e.preventDefault()
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .beginBatch(
              useDocumentStore.getState().document.children,
            )
        }
        for (const id of selectedIds) {
          useDocumentStore.getState().reorderNode(id, 'down')
        }
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .endBatch(useDocumentStore.getState().document)
        }
        return
      }
      if (e.key === ']') {
        e.preventDefault()
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .beginBatch(
              useDocumentStore.getState().document.children,
            )
        }
        for (const id of selectedIds) {
          useDocumentStore.getState().reorderNode(id, 'up')
        }
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .endBatch(useDocumentStore.getState().document)
        }
        return
      }

      // Arrow keys: nudge
      const nudgeKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']
      if (nudgeKeys.includes(e.key) && !isMod) {
        const { selectedIds } = useCanvasStore.getState().selection
        if (selectedIds.length === 0) return
        e.preventDefault()
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .beginBatch(
              useDocumentStore.getState().document.children,
            )
        }
        const amount = e.shiftKey ? 10 : 1
        for (const id of selectedIds) {
          const node = useDocumentStore.getState().getNodeById(id)
          if (!node) continue
          const updates: Record<string, number> = {}
          if (e.key === 'ArrowLeft') updates.x = (node.x ?? 0) - amount
          if (e.key === 'ArrowRight') updates.x = (node.x ?? 0) + amount
          if (e.key === 'ArrowUp') updates.y = (node.y ?? 0) - amount
          if (e.key === 'ArrowDown') updates.y = (node.y ?? 0) + amount
          useDocumentStore.getState().updateNode(id, updates)
        }
        if (selectedIds.length > 1) {
          useHistoryStore
            .getState()
            .endBatch(useDocumentStore.getState().document)
        }
        return
      }
    }

    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [])
}
