/**
 * CalcText main application component.
 *
 * Workspace layout: header → tab bar → editor + results panel.
 * Multi-document support via document store with localStorage persistence.
 */

import { useCallback, useState, useRef, useEffect } from 'react'
import type { EditorView } from '@codemirror/view'
import { CalcEditor } from './editor/CalcEditor.tsx'
import { useEngine } from './engine/useEngine.ts'
import { useOnlineStatus } from './hooks/useOnlineStatus.ts'
import { useInstallPrompt } from './hooks/useInstallPrompt.ts'
import { useTheme } from './hooks/useTheme.ts'
import { useDocumentStore, loadSidebarState, saveSidebarState } from './hooks/useDocumentStore.ts'
import { OfflineBanner } from './components/OfflineBanner.tsx'
import { InstallPrompt } from './components/InstallPrompt.tsx'
import { ResultsPanel } from './components/ResultsPanel.tsx'
import { ThemePicker } from './components/ThemePicker.tsx'
import { TabBar } from './components/TabBar.tsx'
import { Sidebar } from './components/Sidebar.tsx'
import { StatusBar } from './components/StatusBar.tsx'
import { AlignToolbar } from './components/AlignToolbar.tsx'
import type { Alignment } from './components/AlignToolbar.tsx'
import { FormatToolbar } from './components/FormatToolbar.tsx'
import { FontSizeControl } from './components/FontSizeControl.tsx'
import { useFontSize } from './hooks/useFontSize.ts'
import { MobileResultsTray } from './components/MobileResultsTray.tsx'
import { UserMenu } from './components/UserMenu.tsx'
import { SecuritySettings } from './components/SecuritySettings.tsx'
import { useAuth } from './auth/AuthProvider.tsx'
import { AuthModal } from './auth/AuthModal.tsx'
import './styles/app.css'

function App() {
  const engine = useEngine()
  const isOnline = useOnlineStatus()
  const installPrompt = useInstallPrompt()
  const themeCtx = useTheme()
  const fontSizeCtx = useFontSize()
  const auth = useAuth()
  const store = useDocumentStore()

  const [showAuthModal, setShowAuthModal] = useState(false)
  const [showSecuritySettings, setShowSecuritySettings] = useState(false)

  const [editorView, setEditorView] = useState<EditorView | null>(null)
  const resultsPanelRef = useRef<HTMLDivElement>(null)
  const [modifiedIds, setModifiedIds] = useState<Set<string>>(new Set())
  const [editorAlign, setEditorAlign] = useState<Alignment>('left')
  const [resultsAlign, setResultsAlign] = useState<Alignment>('right')
  const [formatPreview, setFormatPreview] = useState(true)

  // Sidebar state
  const [sidebarState, setSidebarState] = useState(loadSidebarState)
  const setSidebarVisible = useCallback((v: boolean) => {
    const next = { ...sidebarState, visible: v }
    setSidebarState(next)
    saveSidebarState(next)
  }, [sidebarState])
  const setSidebarWidth = useCallback((w: number) => {
    const next = { ...sidebarState, width: w }
    setSidebarState(next)
    saveSidebarState(next)
  }, [sidebarState])

  // Track a key to force CalcEditor remount on tab switch
  const [editorKey, setEditorKey] = useState(store.activeTabId)

  // Draggable divider state
  const [dividerX, setDividerX] = useState<number | null>(null)
  const isDragging = useRef(false)

  const handleDocChange = useCallback(
    (lines: string[]) => {
      engine.evalSheet(lines)
      // Persist content
      const content = lines.join('\n')
      if (store.activeDoc && content !== store.activeDoc.content) {
        store.updateContent(store.activeTabId, content)
        setModifiedIds(prev => {
          const next = new Set(prev)
          next.add(store.activeTabId)
          return next
        })
        // Clear modified dot after save debounce
        setTimeout(() => {
          setModifiedIds(prev => {
            const next = new Set(prev)
            next.delete(store.activeTabId)
            return next
          })
        }, 500)
      }
    },
    [engine.evalSheet, store.activeTabId, store.activeDoc, store.updateContent],
  )

  // Switch tabs
  const handleTabClick = useCallback((id: string) => {
    if (id === store.activeTabId) return
    store.setActiveTab(id)
    setEditorKey(id)
  }, [store.activeTabId, store.setActiveTab])

  // New document
  const handleNewTab = useCallback(() => {
    const doc = store.createDocument()
    setEditorKey(doc.id)
  }, [store.createDocument])

  // Close tab
  const handleTabClose = useCallback((id: string) => {
    store.closeTab(id)
    // If we closed the active tab, editorKey needs updating
    if (id === store.activeTabId) {
      // State will update, trigger effect below
    }
  }, [store.closeTab, store.activeTabId])

  // Sync editorKey when activeTabId changes externally (e.g. after close)
  useEffect(() => {
    if (editorKey !== store.activeTabId) {
      setEditorKey(store.activeTabId)
    }
  }, [store.activeTabId]) // eslint-disable-line react-hooks/exhaustive-deps

  // Apply editor text alignment via CodeMirror
  useEffect(() => {
    if (!editorView) return
    editorView.dom.style.setProperty('--cm-text-align', editorAlign)
  }, [editorView, editorAlign])

  // Scroll sync: mirror editor scroll position to results panel
  useEffect(() => {
    if (!editorView) return
    const scroller = editorView.scrollDOM
    const onScroll = () => {
      if (resultsPanelRef.current) {
        resultsPanelRef.current.scrollTop = scroller.scrollTop
      }
    }
    scroller.addEventListener('scroll', onScroll, { passive: true })
    return () => scroller.removeEventListener('scroll', onScroll)
  }, [editorView])

  // Draggable divider handlers
  const onDividerMouseDown = useCallback((e: React.MouseEvent) => {
    e.preventDefault()
    isDragging.current = true
    document.body.style.cursor = 'col-resize'
    document.body.style.userSelect = 'none'
  }, [])

  useEffect(() => {
    function onMouseMove(e: MouseEvent) {
      if (!isDragging.current) return
      setDividerX(e.clientX)
    }
    function onMouseUp() {
      if (!isDragging.current) return
      isDragging.current = false
      document.body.style.cursor = ''
      document.body.style.userSelect = ''
    }
    document.addEventListener('mousemove', onMouseMove)
    document.addEventListener('mouseup', onMouseUp)
    return () => {
      document.removeEventListener('mousemove', onMouseMove)
      document.removeEventListener('mouseup', onMouseUp)
    }
  }, [])

  // Keyboard shortcuts
  useEffect(() => {
    function handleKey(e: KeyboardEvent) {
      const mod = e.metaKey || e.ctrlKey

      // Ctrl+B — toggle sidebar
      if (mod && e.key === 'b') {
        e.preventDefault()
        setSidebarVisible(!sidebarState.visible)
        return
      }

      // Ctrl+N — new document
      if (mod && e.key === 'n') {
        e.preventDefault()
        handleNewTab()
        return
      }

      // Ctrl+W — close tab
      if (mod && e.key === 'w') {
        e.preventDefault()
        handleTabClose(store.activeTabId)
        return
      }

      // Ctrl+Tab / Ctrl+Shift+Tab — cycle tabs
      if (mod && e.key === 'Tab') {
        e.preventDefault()
        const idx = store.openTabIds.indexOf(store.activeTabId)
        const len = store.openTabIds.length
        const next = e.shiftKey
          ? store.openTabIds[(idx - 1 + len) % len]
          : store.openTabIds[(idx + 1) % len]
        handleTabClick(next)
        return
      }

      // Ctrl+1-9 — jump to tab
      if (mod && e.key >= '1' && e.key <= '9') {
        e.preventDefault()
        const tabIdx = parseInt(e.key) - 1
        if (tabIdx < store.openTabIds.length) {
          handleTabClick(store.openTabIds[tabIdx])
        }
        return
      }

      // Ctrl+= — increase font size
      if (mod && (e.key === '=' || e.key === '+')) {
        e.preventDefault()
        fontSizeCtx.setFontSize(fontSizeCtx.fontSize + 1)
        return
      }

      // Ctrl+- — decrease font size
      if (mod && e.key === '-') {
        e.preventDefault()
        fontSizeCtx.setFontSize(fontSizeCtx.fontSize - 1)
        return
      }

      // Ctrl+0 — reset font size
      if (mod && e.key === '0') {
        e.preventDefault()
        fontSizeCtx.resetFontSize()
        return
      }
    }

    document.addEventListener('keydown', handleKey)
    return () => document.removeEventListener('keydown', handleKey)
  }, [store.activeTabId, store.openTabIds, handleNewTab, handleTabClose, handleTabClick, sidebarState.visible, setSidebarVisible, fontSizeCtx])

  // Compute flex styles from divider position
  const editorStyle: React.CSSProperties = dividerX !== null
    ? { width: dividerX, flex: 'none' }
    : {}
  const resultsStyle: React.CSSProperties = dividerX !== null
    ? { flex: 1 }
    : {}

  return (
    <div className="calcpad-app">
      <OfflineBanner isOnline={isOnline} />

      <header className="calcpad-header">
        <button
          className="header-sidebar-toggle"
          onClick={() => setSidebarVisible(!sidebarState.visible)}
          title="Toggle sidebar (Ctrl+B)"
          aria-label="Toggle sidebar"
        >
          ☰
        </button>
        <h1>CalcText</h1>
        <div className="header-spacer" />
        <div className="header-actions">
          <FormatToolbar
            editorView={editorView}
            previewMode={formatPreview}
            onPreviewToggle={() => setFormatPreview(p => !p)}
          />
          <div className="header-divider" />
          <AlignToolbar
            editorAlign={editorAlign}
            resultsAlign={resultsAlign}
            onEditorAlignChange={setEditorAlign}
            onResultsAlignChange={setResultsAlign}
          />
          <FontSizeControl
            fontSize={fontSizeCtx.fontSize}
            onFontSizeChange={fontSizeCtx.setFontSize}
            min={fontSizeCtx.MIN_SIZE}
            max={fontSizeCtx.MAX_SIZE}
          />
          <ThemePicker
            theme={themeCtx.theme}
            resolvedTheme={themeCtx.resolvedTheme}
            accentColor={themeCtx.accentColor}
            onThemeChange={themeCtx.setTheme}
            onAccentChange={themeCtx.setAccent}
          />
          <UserMenu
            onOpenAuth={() => setShowAuthModal(true)}
            onOpenSecurity={() => setShowSecuritySettings(true)}
          />
        </div>
      </header>

      <TabBar
        tabs={store.openDocs}
        activeTabId={store.activeTabId}
        onTabClick={handleTabClick}
        onTabClose={handleTabClose}
        onTabRename={store.renameDocument}
        onNewTab={handleNewTab}
        modifiedIds={modifiedIds}
      />

      <div className="calcpad-workspace">
        {/* Mobile sidebar backdrop */}
        {sidebarState.visible && (
          <div
            className="sidebar-backdrop"
            onClick={() => setSidebarVisible(false)}
          />
        )}
        <Sidebar
          visible={sidebarState.visible}
          width={sidebarState.width}
          documents={store.documents}
          folders={store.folders}
          activeTabId={store.activeTabId}
          openTabIds={store.openTabIds}
          onFileClick={(id) => { store.openDocument(id); setEditorKey(id) }}
          onNewDocument={(title, content) => {
            const doc = store.createDocument(title, content)
            setEditorKey(doc.id)
          }}
          onNewFolder={() => store.createFolder()}
          onRenameDocument={store.renameDocument}
          onDeleteDocument={store.deleteDocument}
          onToggleFavorite={store.toggleFavorite}
          onMoveToFolder={store.moveToFolder}
          onRenameFolder={store.renameFolder}
          onDeleteFolder={store.deleteFolder}
          onWidthChange={setSidebarWidth}
        />

        <main className="calcpad-editor">
          <div className="editor-pane" style={editorStyle}>
            <CalcEditor
              key={editorKey}
              initialDoc={store.activeDoc?.content ?? ''}
              onDocChange={handleDocChange}
              results={engine.results}
              debounceMs={50}
              onViewReady={setEditorView}
              formatPreview={formatPreview}
            />
          </div>
          <div
            className="pane-divider"
            onMouseDown={onDividerMouseDown}
          />
          <ResultsPanel
            ref={resultsPanelRef}
            results={engine.results}
            align={resultsAlign}
            style={resultsStyle}
          />
        </main>
      </div>

      <MobileResultsTray
        results={engine.results}
        docLines={store.activeDoc?.content.split('\n') ?? []}
      />

      <StatusBar
        editorView={editorView}
        engineReady={engine.ready}
        lineCount={store.activeDoc?.content.split('\n').length ?? 0}
      />

      <InstallPrompt
        promptEvent={installPrompt.promptEvent}
        isInstalled={installPrompt.isInstalled}
        onInstall={installPrompt.handleInstall}
        onDismiss={installPrompt.handleDismiss}
      />

      {showAuthModal && (
        <AuthModal onClose={() => setShowAuthModal(false)} />
      )}

      {auth.needsPasswordRenewal && (
        <AuthModal
          onClose={() => auth.clearPasswordRenewal()}
          renewalMode
        />
      )}

      {showSecuritySettings && (
        <SecuritySettings onClose={() => setShowSecuritySettings(false)} />
      )}
    </div>
  )
}

export default App
