import { useEffect, useState, useRef, Component, type ReactNode } from "react"
import { Toolbar } from "./components/Toolbar"
import { TitleBar } from "./components/TitleBar"
import { LeftSidebar } from "./components/LeftSidebar"
import { CanvasView } from "./components/CanvasView"
import { PropertiesPanel } from "./components/PropertiesPanel"
import { StatusBar } from "./components/StatusBar"
import { KeyboardCheatSheet } from "./components/KeyboardCheatSheet"
import { ResizeHandle } from "./components/ResizeHandle"
import { TabBar } from "./components/TabBar"
import { ApiClientPanel } from "./components/ApiClient/ApiClientPanel"
import { useStore } from "./store/useStore"
import { loadTemplateDialog } from "./utils/loadFile"
import type { AppTab } from "./components/TabBar"

const MIN_SIDEBAR = 160
const MAX_SIDEBAR = 500

class TabErrorBoundary extends Component<{ children: ReactNode; tab: string }, { hasError: boolean; error: string }> {
  constructor(props: { children: ReactNode; tab: string }) {
    super(props)
    this.state = { hasError: false, error: "" }
  }
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error: error.message || String(error) }
  }
  render() {
    if (this.state.hasError) {
      return (
        <div className="p-8 text-error bg-error/5 border border-error/20 rounded-lg m-4">
          <h2 className="text-title-md font-medium mb-2">{this.props.tab} Tab Crashed</h2>
          <pre className="text-code whitespace-pre-wrap">{this.state.error}</pre>
        </div>
      )
    }
    return this.props.children
  }
}

export default function App() {
  const containerRef = useRef<HTMLDivElement>(null)
  const [showCheatSheet, setShowCheatSheet] = useState(false)
  const [activeTab, setActiveTab] = useState<AppTab>("editor")
  const [leftWidth, setLeftWidth] = useState(280)
  const [rightWidth, setRightWidth] = useState(320)
  const [leftCollapsed, setLeftCollapsed] = useState(false)
  const [rightCollapsed, setRightCollapsed] = useState(false)
  const setToolMode = useStore((s) => s.setToolMode)
  const undo = useStore((s) => s.undo)
  const deleteSection = useStore((s) => s.deleteSection)
  const selectedSectionIdx = useStore((s) => s.selectedSectionIdx)
  const toolMode = useStore((s) => s.toolMode)
  const currentBlockName = useStore((s) => s.currentBlockName)
  const darkMode = useStore((s) => s.darkMode)

  useEffect(() => {
    document.documentElement.classList.toggle("dark", darkMode)
    window.electronAPI?.setTheme(darkMode)
  }, [darkMode])

  useEffect(() => {
    const onKeyDown = (e: KeyboardEvent) => {
      const target = e.target as HTMLElement
      if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT") return

      const ctrl = e.ctrlKey || e.metaKey

      if (e.key === "Escape") { setShowCheatSheet(false); return }
      if (e.key === "?") { e.preventDefault(); setShowCheatSheet((v) => !v); return }

      switch (e.key.toLowerCase()) {
        case "s": {
          e.preventDefault()
          if (ctrl) {
            const state = useStore.getState()
            const errors = state.validateExport()
            if (errors.length > 0) {
              alert("Validation errors:\n" + errors.map((e) => `  - ${e}`).join("\n"))
              return
            }
            const data = state.buildExportData()
            const json = JSON.stringify(data, null, 2)
            if (window.electronAPI) {
              window.electronAPI.saveFileDialog("metadata.json", [{ name: "JSON", extensions: ["json"] }])
                .then((path: string) => {
                  if (!path) { state.setStatusMessage("Export cancelled"); return }
                  return window.electronAPI!.writeFile(path, json)
                })
                .then(() => state.setStatusMessage("Exported successfully"))
                .catch((err: Error) => { state.setStatusMessage("Export failed"); console.error(err) })
            } else {
              const blob = new Blob([json], { type: "application/json" })
              const url = URL.createObjectURL(blob)
              const a = document.createElement("a")
              a.href = url
              a.download = "metadata.json"
              a.click()
              URL.revokeObjectURL(url)
              state.setStatusMessage("Exported metadata.json")
            }
          } else {
            setToolMode("select")
          }
          break
        }
        case "o":
          if (ctrl) {
            e.preventDefault()
            loadTemplateDialog().then((result) => {
              if (!result) return
              useStore.getState().setTemplate(result.dataUrl, result.filePath, result.width, result.height)
              useStore.getState().setStatusMessage(`Loaded: ${result.name}`)
            })
          }
          break
        case "z":
          if (ctrl) {
            e.preventDefault()
            undo()
          }
          break
        case "delete":
        case "backspace":
          if (selectedSectionIdx !== null) {
            e.preventDefault()
            deleteSection(selectedSectionIdx)
          }
          break
        case "d":
          if (!ctrl) { e.preventDefault(); setToolMode("draw") }
          break
        case "p":
          if (!ctrl) { e.preventDefault(); setToolMode("pan") }
          break
        case "arrowup":
        case "arrowdown":
        case "arrowleft":
        case "arrowright":
          if (toolMode === "select" && selectedSectionIdx !== null && currentBlockName) {
            e.preventDefault()
            const step = e.shiftKey ? 10 : 1
            const state = useStore.getState()
            const block = state.blocks[currentBlockName]
            if (!block) break
            const roi = block.sections[selectedSectionIdx]
            if (!roi) break
            const [x1, x2, y1, y2] = roi.cords
            let nx1 = x1, nx2 = x2, ny1 = y1, ny2 = y2
            switch (e.key.toLowerCase()) {
              case "arrowup": ny1 -= step; ny2 -= step; break
              case "arrowdown": ny1 += step; ny2 += step; break
              case "arrowleft": nx1 -= step; nx2 -= step; break
              case "arrowright": nx1 += step; nx2 += step; break
            }
            state.pushUndo()
            state.updateSectionCords(selectedSectionIdx, [nx1, nx2, ny1, ny2])
            state.setStatusMessage(`Nudged to (${nx1}, ${ny1})`)
          }
          break
      }
    }

    window.addEventListener("keydown", onKeyDown)
    return () => window.removeEventListener("keydown", onKeyDown)
  }, [setToolMode, undo, deleteSection, selectedSectionIdx, toolMode, currentBlockName])

  return (
    <div className="h-screen flex flex-col overflow-hidden">
      <TitleBar />
      <TabBar active={activeTab} onChange={setActiveTab} />
      {activeTab === "editor" ? (
        <TabErrorBoundary tab="Editor" key="editor">
          <Toolbar />
          <div ref={containerRef} className="flex flex-1 overflow-hidden">
            <LeftSidebar width={leftCollapsed ? 32 : leftWidth} collapsed={leftCollapsed} onToggle={() => setLeftCollapsed((v) => !v)} />
            {!leftCollapsed && <ResizeHandle onResize={(clientX) => setLeftWidth(Math.max(MIN_SIDEBAR, Math.min(MAX_SIDEBAR, clientX)))} />}
            <CanvasView />
            {!rightCollapsed && <ResizeHandle onResize={(clientX) => {
              if (!containerRef.current) return
              const cr = containerRef.current.getBoundingClientRect()
              setRightWidth(Math.max(MIN_SIDEBAR, Math.min(MAX_SIDEBAR, cr.right - clientX)))
            }} />}
            <PropertiesPanel width={rightCollapsed ? 32 : rightWidth} collapsed={rightCollapsed} onToggle={() => setRightCollapsed((v) => !v)} />
          </div>
        </TabErrorBoundary>
      ) : (
        <TabErrorBoundary tab="API Client" key="api">
          <div className="flex-1 overflow-hidden bg-canvas">
            <ApiClientPanel />
          </div>
        </TabErrorBoundary>
      )}
      <StatusBar />
      {showCheatSheet && <KeyboardCheatSheet onClose={() => setShowCheatSheet(false)} />}
    </div>
  )
}
