import { useCallback, useEffect, useMemo, lazy, Suspense } from 'react'
import { Header } from './Header'
import { LeftSidebar } from './LeftSidebar'
import { MainPane } from './MainPane'
import { RightSidebar } from './RightSidebar'
import { BottomDock } from './BottomDock'
import { Toaster } from '@/components/ui/sonner'

const SessionHistory = lazy(() =>
  import('@/components/sessions/SessionHistory').then((m) => ({ default: m.SessionHistory }))
)
const CommandPalette = lazy(() =>
  import('@/components/command-palette').then((m) => ({ default: m.CommandPalette }))
)
const SettingsModal = lazy(() =>
  import('@/components/settings').then((m) => ({ default: m.SettingsModal }))
)
const AgentSetupGuard = lazy(() =>
  import('@/components/setup').then((m) => ({ default: m.AgentSetupGuard }))
)
const FdaSetupGuard = lazy(() =>
  import('@/components/setup').then((m) => ({ default: m.FdaSetupGuard }))
)
const HelpOverlay = lazy(() =>
  import('@/components/ui/HelpOverlay').then((m) => ({ default: m.HelpOverlay }))
)
const FileSearchDialog = lazy(() =>
  import('@/components/file-search').then((m) => ({ default: m.FileSearchDialog }))
)
import { useKeyboardShortcuts } from '@/hooks/useKeyboardShortcuts'
import { useVimNavigation } from '@/hooks/useVimNavigation'
import { useAgentEventBridge } from '@/hooks/useAgentEventBridge'
import { useNotificationNavigation } from '@/hooks/useNotificationNavigation'
import { useWindowFocusRefresh } from '@/hooks/useWindowFocusRefresh'
import { useWorktreeWatcher } from '@/hooks/useWorktreeWatcher'
import { useConnectionWatcher } from '@/hooks/useConnectionWatcher'
import { useAutoUpdate } from '@/hooks/useAutoUpdate'
import { useKeepAwake } from '@/hooks/useKeepAwake'
import { ErrorBoundary, ErrorFallback } from '@/components/error'
import { ProjectSettingsDialog } from '@/components/projects/ProjectSettingsDialog'
import { useProjectStore } from '@/stores/useProjectStore'
import { useWorktreeStore } from '@/stores/useWorktreeStore'
import { useGitStore } from '@/stores/useGitStore'
import { useSessionStore } from '@/stores/useSessionStore'
import { useDropZone } from '@/hooks/useDropZone'
import { DropOverlay } from './DropOverlay'
import { toast } from '@/lib/toast'
import { useDropAttachmentStore } from '@/stores'
import { useLayoutStore } from '@/stores/useLayoutStore'
import { MAX_ATTACHMENTS, isImageMime } from '@/lib/file-attachment-utils'
import type { Attachment } from '@/components/sessions/AttachmentPreview'
import { useI18n } from '@/i18n/useI18n'

function GlobalProjectSettings(): React.JSX.Element | null {
  const settingsProjectId = useProjectStore((s) => s.settingsProjectId)
  const closeProjectSettings = useProjectStore((s) => s.closeProjectSettings)
  const project = useProjectStore((s) => s.projects.find((p) => p.id === s.settingsProjectId))

  if (!project) return null

  return (
    <ProjectSettingsDialog
      project={project}
      open={!!settingsProjectId}
      onOpenChange={(open) => {
        if (!open) closeProjectSettings()
      }}
    />
  )
}

interface AppLayoutProps {
  children?: React.ReactNode
}

export function AppLayout({ children }: AppLayoutProps): React.JSX.Element {
  const { t } = useI18n()
  // Register all keyboard shortcuts centrally
  useKeyboardShortcuts()
  // Vim-style modal navigation (hjkl, panel shortcuts, file tab cycling)
  useVimNavigation()
  // Global listener for background session events (AI finishes while viewing another project)
  useAgentEventBridge()
  // Navigate to session when native notification is clicked
  useNotificationNavigation()
  // Refresh git statuses when window regains focus
  useWindowFocusRefresh()
  // Watch active worktree for filesystem + .git changes (main-process watcher)
  useWorktreeWatcher()
  // Watch connection member worktrees for filesystem + .git changes
  useConnectionWatcher()
  // Auto-update notifications
  useAutoUpdate()
  // Keep display awake while opted-in sessions are actively running
  useKeepAwake()

  // Drag-and-drop from Finder
  const activeSessionId = useSessionStore((s) => s.activeSessionId)

  const handleFileDrop = useCallback(
    (files: FileList) => {
      const sessionId = useSessionStore.getState().activeSessionId
      if (!sessionId) {
        toast.warning(t('appLayout.drop.noSession'))
        return
      }

      const allFiles = Array.from(files)

      // Filter out directories (in Electron, directories have type '' and size 0)
      const validFiles = allFiles.filter((f) => {
        if (f.type === '' && f.size === 0) {
          return false
        }
        return true
      })

      if (validFiles.length < allFiles.length) {
        toast.warning(t('appLayout.drop.noFolders'))
      }

      if (validFiles.length === 0) return

      // Truncate to max
      const filesToProcess =
        validFiles.length > MAX_ATTACHMENTS
          ? (toast.warning(t('appLayout.drop.maxFiles', { count: MAX_ATTACHMENTS })),
            validFiles.slice(0, MAX_ATTACHMENTS))
          : validFiles

      // Process files
      const promises = filesToProcess.map((file) => {
        if (isImageMime(file.type)) {
          return new Promise<Omit<Attachment, 'id'>>((resolve, reject) => {
            const reader = new FileReader()
            reader.onload = () => {
              resolve({
                kind: 'data' as const,
                name: file.name,
                mime: file.type,
                dataUrl: reader.result as string
              })
            }
            reader.onerror = () => {
              reject(new Error(`Failed to read file: ${file.name}`))
            }
            reader.readAsDataURL(file)
          })
        }
        return Promise.resolve({
          kind: 'path' as const,
          name: file.name,
          mime: file.type || 'application/octet-stream',
          filePath: window.fileOps.getPathForFile(file)
        } as Omit<Attachment, 'id'>)
      })

      Promise.all(promises)
        .then((items) => {
          useDropAttachmentStore.getState().push(items)
        })
        .catch((err) => {
          console.error('Failed to process dropped files:', err)
          toast.error(t('appLayout.drop.readError'))
        })
    },
    [t]
  )

  const { isDragging } = useDropZone({ onDrop: handleFileDrop })

  // Check remote info on worktree selection (for PR feature)
  const selectedWorktreeId = useWorktreeStore((s) => s.selectedWorktreeId)
  const worktreesByProject = useWorktreeStore((s) => s.worktreesByProject)
  const selectedWorktreePath = useMemo(() => {
    if (!selectedWorktreeId) return null
    for (const worktrees of worktreesByProject.values()) {
      const wt = worktrees.find((w) => w.id === selectedWorktreeId)
      if (wt) return wt.path
    }
    return null
  }, [selectedWorktreeId, worktreesByProject])

  useEffect(() => {
    if (!selectedWorktreeId || !selectedWorktreePath) return
    const info = useGitStore.getState().remoteInfo.get(selectedWorktreeId)
    if (!info) {
      useGitStore.getState().checkRemoteInfo(selectedWorktreeId, selectedWorktreePath)
    }
  }, [selectedWorktreeId, selectedWorktreePath])

  const terminalDock = useLayoutStore((s) => s.terminalDock)

  return (
    <div className="h-screen flex flex-col bg-background text-foreground" data-testid="app-layout">
      <ErrorBoundary componentName="Header" fallback={<div className="h-12 bg-muted" />}>
        <Header />
      </ErrorBoundary>
      <div className="flex-1 flex flex-col min-h-0" data-testid="layout-content">
        <div className="flex-1 flex min-h-0">
          <ErrorBoundary
            componentName="LeftSidebar"
            fallback={
              <div className="w-60 border-r bg-muted/50 flex items-center justify-center">
                <ErrorFallback compact title={t('appLayout.sidebarError')} />
              </div>
            }
          >
            <LeftSidebar />
          </ErrorBoundary>
          <ErrorBoundary componentName="MainPane">
            <MainPane>{children}</MainPane>
          </ErrorBoundary>
          <ErrorBoundary
            componentName="RightSidebar"
            fallback={<div className="border-l bg-muted/50" />}
          >
            <RightSidebar />
          </ErrorBoundary>
        </div>
        {terminalDock === 'bottom' && (
          <ErrorBoundary componentName="BottomDock" fallback={null}>
            <BottomDock />
          </ErrorBoundary>
        )}
      </div>
      <Toaster />
      {isDragging && <DropOverlay variant={activeSessionId ? 'normal' : 'warning'} />}
      <Suspense fallback={null}>
        <ErrorBoundary componentName="SessionHistory" fallback={null}>
          <SessionHistory />
        </ErrorBoundary>
        <ErrorBoundary componentName="CommandPalette" fallback={null}>
          <CommandPalette />
        </ErrorBoundary>
        <ErrorBoundary componentName="SettingsModal" fallback={null}>
          <SettingsModal />
        </ErrorBoundary>
        <ErrorBoundary componentName="FileSearchDialog" fallback={null}>
          <FileSearchDialog />
        </ErrorBoundary>
        <GlobalProjectSettings />
        <AgentSetupGuard />
        <FdaSetupGuard />
        <HelpOverlay />
      </Suspense>
    </div>
  )
}
