import type { PastedImage } from './components/NewIssueModal'
import type { Issue } from './types'
import { useEffect, useMemo, useRef, useState } from 'react'
import { BottomTabs } from './components/BottomTabs'
import { KanbanBoard } from './components/KanbanBoard'
import { LogModal } from './components/LogModal'
import { NewIssueModal } from './components/NewIssueModal'
import { PanelHeader } from './components/PanelHeader'
import { SettingsModal } from './components/SettingsModal'
import { ToastStack } from './components/ToastStack'
import { useIssues } from './hooks/useIssues'
import { useManagedSessions } from './hooks/useManagedSessions'
import { useProfiles } from './hooks/useProfiles'
import { compareIssuesInColumn } from './lib/issueSort'
import { COLUMN_ORDER } from './types'

export function App() {
  const {
    state,
    settings,
    globalAutoReview,
    youtrackConfigured,
    importYouTrack,
    toasts,
    profiles,
    scope,
    toggleScope,
    setIssues,
    refresh,
    saveSettings,
    listYouTrackProjects,
    youtrackProjects,
    dismissSettings,
    requestEditAuth,
    createIssue,
    dismissToast,
    openUrl,
    resumeSession,
    resumeReviewSession,
    startTestSession,
    resumeTestSession,
    focusSession,
    openFile,
    implement,
    generatePrDiffSummary,
    isPrDiffSummaryRunning,
    openPr,
    openWorktree,
    deleteWorktree,
    mergeBranch,
    deleteIssue,
    closeIssue,
    closeSessionTab,
    startBrainstormSession,
    changeColumn,
    setDependency,
    clearDependency,
    updateIssueAutoReview,
    updateIssueProfilePath,
    updateIssueBrainstormProfilePath,
    updateIssueTestProfilePath,
    logs,
    clearLogs,
    pendingSelectId,
    clearPendingSelect,
    commitRunning,
    runCommit,
    hasChanges,
    branchSyncBehind,
    branchSyncRunning,
    branchSyncDisabled,
    branchSyncTitle,
    runBranchSync,
    envLocked,
    envFileCount,
    envLockRunning,
    envLockTitle,
    toggleEnvLock,
  } = useIssues()
  const { profiles: profileData, saveProfiles, openProfileValue } = useProfiles()
  const {
    managedSessions,
    createManagedSession,
    renameManagedSession,
    resumeManagedSession,
    deleteManagedSession,
    closeManagedSessionTab,
    listImportableNamedSessions,
    importNamedSession,
    importableSessions,
    importableLoading,
  } = useManagedSessions()
  const [showNewIssueModal, setShowNewIssueModal] = useState(false)
  const [showLogs, setShowLogs] = useState(false)
  const [selectedId, setSelectedId] = useState<string | null>(null)
  // 程序化设置的选中（反向选中 / pendingSelectId）记在这里，让下面的自动聚焦
  // effect 跳过它——否则 选中→聚焦→终端激活→反向选中 会死循环、CPU 飙升。
  const lastProgrammaticSelectRef = useRef<string | null>(null)

  function handleSubmitNewIssue(userRequest: string, images: PastedImage[], brainstormProfilePath?: string): void {
    const payload = images.map(({ mediaType, base64 }) => ({ mediaType, base64 }))
    createIssue(userRequest, payload.length > 0 ? payload : undefined, brainstormProfilePath)
    setShowNewIssueModal(false)
  }

  // Drop selectedId if it's no longer present after issues refresh.
  const readyIssues = state.status === 'ready' ? state.issues : null
  useEffect(() => {
    if (!readyIssues)
      return
    if (selectedId && !readyIssues.some(i => i.id === selectedId))
      setSelectedId(null)
  }, [readyIssues, selectedId])

  // Auto-select issues that arrive via webhook in response to user-initiated
  // creation (issue/append with select: true). User-initiated, so we
  // intentionally override any current arrow-key selection.
  useEffect(() => {
    if (!pendingSelectId)
      return
    if (!readyIssues)
      return
    if (!readyIssues.some(i => i.id === pendingSelectId))
      return
    lastProgrammaticSelectRef.current = pendingSelectId
    setSelectedId(pendingSelectId)
    clearPendingSelect()
  }, [pendingSelectId, readyIssues, clearPendingSelect])

  // Issues partitioned by column, in COLUMN_ORDER. Used for arrow navigation.
  // 排序与看板视觉一致：完成列按 PR 合并时间降序，其余列按工单号降序。
  const ordered = useMemo<Issue[][]>(() => {
    if (!readyIssues)
      return []
    return COLUMN_ORDER.map(col =>
      readyIssues.filter(i => i.column === col).sort((a, b) => compareIssuesInColumn(col, a, b)),
    )
  }, [readyIssues])

  // Resolved selected issue (or null) for the detail panel.
  const selectedIssue = useMemo<Issue | null>(() => {
    if (!readyIssues || !selectedId)
      return null
    return readyIssues.find(i => i.id === selectedId) ?? null
  }, [readyIssues, selectedId])

  // Auto-focus the terminal tab whenever the selected card has a session.
  // 优先 implementSessionId（实施终端），没有再回退到 sessionId（规划/头脑风暴终端）；
  // 依赖里加入 implementSessionId，让"先有规划、后有实施"的场景能正确切换。
  useEffect(() => {
    if (!selectedIssue)
      return
    // 这次选中是程序化设置的（反向选中等）→ 不聚焦终端，打断死循环。仅用户主动
    // 点卡片/方向键切换才走聚焦。按 id 匹配，避免同一工单反向选中后残留的标记
    // 误压制后续的用户选中。
    if (selectedIssue.id === lastProgrammaticSelectRef.current) {
      lastProgrammaticSelectRef.current = null
      return
    }
    if (selectedIssue.implementSessionId || selectedIssue.sessionId)
      focusSession(selectedIssue.number)
  }, [selectedIssue?.implementSessionId, selectedIssue?.sessionId, selectedIssue?.number, focusSession])

  // Global keyboard navigation. Skipped whenever an overlay (new-issue, logs,
  // settings) is showing, so its inputs receive arrow/Enter without
  // interference.
  const settingsOpen = settings !== null
  useEffect(() => {
    if (state.status !== 'ready')
      return
    if (showNewIssueModal)
      return
    if (showLogs)
      return
    if (settingsOpen)
      return

    function onKeyDown(e: KeyboardEvent): void {
      // Skip when typing in form fields.
      const active = document.activeElement
      const tag = active?.tagName
      if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT')
        return
      // Editable elements (e.g. contenteditable divs).
      if (active && (active as HTMLElement).isContentEditable)
        return
      // 焦点在提交列表（role=listbox）等自管键盘的区域时，让它自己处理方向键。
      if (active && (active as HTMLElement).closest?.('[role="listbox"]'))
        return

      if (e.key === 'Escape') {
        if (selectedId !== null) {
          e.preventDefault()
          setSelectedId(null)
        }
        return
      }

      if (e.key === 'Enter') {
        const sid = selectedIssue?.implementSessionId || selectedIssue?.sessionId
        if (sid) {
          e.preventDefault()
          const isImpl = !!selectedIssue?.implementSessionId
          const cwd = isImpl ? selectedIssue?.worktreePath : undefined
          // 实施 resume 用 profilePath；头脑风暴 resume 用 brainstormProfilePath。
          const profileForResume = isImpl
            ? selectedIssue?.profilePath
            : selectedIssue?.brainstormProfilePath
          resumeSession(sid, profileForResume, cwd, selectedIssue?.number)
        }
        return
      }

      if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight' && e.key !== 'ArrowUp' && e.key !== 'ArrowDown')
        return

      // Locate current selection within `ordered`.
      let colIdx = -1
      let rowIdx = -1
      if (selectedId) {
        for (let c = 0; c < ordered.length; c++) {
          const r = ordered[c].findIndex(i => i.id === selectedId)
          if (r >= 0) {
            colIdx = c
            rowIdx = r
            break
          }
        }
      }

      // No current selection: pick the first card in the first non-empty
      // column (typically todo[0]).
      if (colIdx < 0) {
        for (let c = 0; c < ordered.length; c++) {
          if (ordered[c].length > 0) {
            e.preventDefault()
            setSelectedId(ordered[c][0].id)
            return
          }
        }
        return
      }

      if (e.key === 'ArrowUp') {
        if (rowIdx > 0) {
          e.preventDefault()
          setSelectedId(ordered[colIdx][rowIdx - 1].id)
        }
        return
      }

      if (e.key === 'ArrowDown') {
        if (rowIdx < ordered[colIdx].length - 1) {
          e.preventDefault()
          setSelectedId(ordered[colIdx][rowIdx + 1].id)
        }
        return
      }

      // Horizontal navigation: keep rowIdx, clamp to target column length - 1.
      // Skip empty columns entirely.
      const dir = e.key === 'ArrowLeft' ? -1 : 1
      let target = colIdx + dir
      while (target >= 0 && target < ordered.length && ordered[target].length === 0)
        target += dir
      if (target < 0 || target >= ordered.length)
        return
      e.preventDefault()
      const clampedRow = Math.min(rowIdx, ordered[target].length - 1)
      setSelectedId(ordered[target][clampedRow].id)
    }

    window.addEventListener('keydown', onKeyDown)
    return () => window.removeEventListener('keydown', onKeyDown)
  }, [state.status, showNewIssueModal, showLogs, settingsOpen, ordered, selectedId, selectedIssue, resumeSession])

  // When the user is being forced into first-time setup, the kanban behind
  // the modal has nothing meaningful to show — replace the spinner with a
  // muted hint so it doesn't look like things are still loading.
  const blockedBySetup = settings !== null && !settings.canCancel && state.status === 'loading'

  return (
    <div className="flex h-screen w-screen flex-col overflow-hidden bg-[var(--vscode-editor-background)]">
      {state.status === 'loading' && (
        <>
          <PanelHeader
            onRefresh={refresh}
            scope={scope}
            onToggleScope={toggleScope}
            onEditAuth={requestEditAuth}
            commitRunning={commitRunning}
            hasChanges={hasChanges}
            onCommit={runCommit}
            branchSyncBehind={branchSyncBehind}
            branchSyncRunning={branchSyncRunning}
            branchSyncDisabled={branchSyncDisabled}
            branchSyncTitle={branchSyncTitle}
            envLocked={envLocked}
            envFileCount={envFileCount}
            envLockRunning={envLockRunning}
            envLockTitle={envLockTitle}
            onToggleEnvLock={toggleEnvLock}
            onSyncBranch={runBranchSync}
            youtrackConfigured={youtrackConfigured}
            onImportYouTrack={importYouTrack}
          />
          <div className="flex h-full w-full items-center justify-center text-sm opacity-70">
            {blockedBySetup ? '请先完成设置' : '加载中…'}
          </div>
        </>
      )}

      {state.status === 'ready' && (
        <>
          <PanelHeader
            onRefresh={refresh}
            scope={scope}
            onToggleScope={toggleScope}
            onEditAuth={requestEditAuth}
            commitRunning={commitRunning}
            hasChanges={hasChanges}
            onCommit={runCommit}
            branchSyncBehind={branchSyncBehind}
            branchSyncRunning={branchSyncRunning}
            branchSyncDisabled={branchSyncDisabled}
            branchSyncTitle={branchSyncTitle}
            envLocked={envLocked}
            envFileCount={envFileCount}
            envLockRunning={envLockRunning}
            envLockTitle={envLockTitle}
            onToggleEnvLock={toggleEnvLock}
            onSyncBranch={runBranchSync}
            youtrackConfigured={youtrackConfigured}
            onImportYouTrack={importYouTrack}
          />
          <div className="flex min-h-0 flex-1 flex-col">
            <div className="min-h-0 flex-1 overflow-hidden">
              <KanbanBoard
                issues={state.issues}
                onIssuesChange={setIssues}
                onCreateIssue={() => setShowNewIssueModal(true)}
                selectedId={selectedId}
                onSelectIssue={setSelectedId}
                onColumnChange={changeColumn}
                onDependencySet={setDependency}
                onDependencyClear={clearDependency}
              />
            </div>
            <div className="h-[40vh] shrink-0">
              <BottomTabs
                issue={selectedIssue}
                allIssues={state.issues}
                globalAutoReview={globalAutoReview}
                onOpenInBrowser={openUrl}
                onResumeSession={resumeSession}
                onResumeReviewSession={resumeReviewSession}
                onStartTestSession={startTestSession}
                onResumeTestSession={resumeTestSession}
                onOpenFile={openFile}
                onImplement={implement}
                onOpenPr={openPr}
                onGeneratePrDiffSummary={generatePrDiffSummary}
                isPrDiffSummaryRunning={isPrDiffSummaryRunning}
                onOpenWorktree={openWorktree}
                onDeleteWorktree={deleteWorktree}
                onMergeBranch={mergeBranch}
                onDeleteIssue={deleteIssue}
                onCloseIssue={closeIssue}
                onCloseSessionTab={closeSessionTab}
                onStartBrainstormSession={startBrainstormSession}
                onUpdateAutoReview={updateIssueAutoReview}
                onUpdateProfilePath={updateIssueProfilePath}
                onUpdateBrainstormProfilePath={updateIssueBrainstormProfilePath}
                onUpdateTestProfilePath={updateIssueTestProfilePath}
                onOpenLogs={() => setShowLogs(true)}
                profileData={profileData}
                onProfileSave={saveProfiles}
                onProfileOpen={openProfileValue}
                managedSessions={managedSessions}
                profiles={profiles}
                onManagedSessionCreate={createManagedSession}
                onManagedSessionRename={renameManagedSession}
                onManagedSessionResume={resumeManagedSession}
                onManagedSessionDelete={deleteManagedSession}
                onManagedSessionCloseTab={closeManagedSessionTab}
                importableSessions={importableSessions}
                importableLoading={importableLoading}
                onListImportableSessions={listImportableNamedSessions}
                onImportNamedSession={importNamedSession}
              />
            </div>
          </div>
        </>
      )}

      {state.status === 'error' && (
        <>
          <PanelHeader
            onRefresh={refresh}
            scope={scope}
            onToggleScope={toggleScope}
            onEditAuth={requestEditAuth}
            commitRunning={commitRunning}
            hasChanges={hasChanges}
            onCommit={runCommit}
            branchSyncBehind={branchSyncBehind}
            branchSyncRunning={branchSyncRunning}
            branchSyncDisabled={branchSyncDisabled}
            branchSyncTitle={branchSyncTitle}
            envLocked={envLocked}
            envFileCount={envFileCount}
            envLockRunning={envLockRunning}
            envLockTitle={envLockTitle}
            onToggleEnvLock={toggleEnvLock}
            onSyncBranch={runBranchSync}
            youtrackConfigured={youtrackConfigured}
            onImportYouTrack={importYouTrack}
          />
          <div className="flex flex-1 items-center justify-center p-6">
            <div className="max-w-md rounded-md border border-state-red/40 bg-state-red/10 p-4 text-sm">
              <div className="mb-3 font-medium text-state-red">无法加载 issues</div>
              <div className="mb-4 whitespace-pre-wrap opacity-90">{state.message}</div>
              <div className="flex gap-3">
                <button
                  type="button"
                  onClick={refresh}
                  className="rounded border border-state-red/60 px-3 py-1 text-xs hover:bg-state-red/20"
                >
                  重试
                </button>
                <button
                  type="button"
                  onClick={requestEditAuth}
                  className="text-xs text-[var(--vscode-textLink-foreground)] underline"
                >
                  重新配置
                </button>
              </div>
            </div>
          </div>
        </>
      )}

      <NewIssueModal
        open={showNewIssueModal}
        onCancel={() => setShowNewIssueModal(false)}
        onSubmit={handleSubmitNewIssue}
        profiles={profiles}
      />
      <LogModal
        open={showLogs}
        entries={logs}
        onClose={() => setShowLogs(false)}
        onClear={clearLogs}
      />
      <SettingsModal
        open={settings !== null}
        host={settings?.host ?? ''}
        errorMessage={settings?.errorMessage}
        canCancel={settings?.canCancel}
        initialTokenSaved={settings?.tokenSaved ?? false}
        initialWebhookPort={settings?.webhookPort ?? 17421}
        initialWebhookSecret={settings?.webhookSecret ?? ''}
        initialBrainstormPrompt={settings?.brainstormPrompt ?? ''}
        initialBrainstormContinuePrompt={settings?.brainstormContinuePrompt ?? ''}
        initialImplementPlanPrompt={settings?.implementPlanPrompt ?? ''}
        initialAutoReview={settings?.autoReview ?? true}
        initialReviewPrompt={settings?.reviewPrompt ?? ''}
        initialDevBranch={settings?.devBranch ?? 'main'}
        initialAutoBuildBranch={settings?.autoBuildBranch ?? ''}
        initialWorktreeDirectory={settings?.worktreeDirectory ?? ''}
        initialWorktreeOpenWith={settings?.worktreeOpenWith ?? 'vscode'}
        initialPycharmCommand={settings?.pycharmCommand ?? 'pycharm'}
        initialWorktreePostCreateScript={settings?.worktreePostCreateScript ?? ''}
        initialWorktreePreRemoveScript={settings?.worktreePreRemoveScript ?? ''}
        initialImplTabPreCreateScript={settings?.implTabPreCreateScript ?? ''}
        initialImplTabPostCloseScript={settings?.implTabPostCloseScript ?? ''}
        initialConflictResolutionProfilePath={settings?.conflictResolutionProfilePath ?? ''}
        initialCommitProfilePath={settings?.commitProfilePath ?? ''}
        initialCodexModel={settings?.codexModel ?? ''}
        initialCodexReasoningEffort={settings?.codexReasoningEffort ?? ''}
        initialProfilesDirectory={settings?.profilesDirectory ?? ''}
        initialYoutrackBaseUrl={settings?.youtrackBaseUrl ?? ''}
        initialYoutrackProjectShortName={settings?.youtrackProjectShortName ?? ''}
        initialYoutrackCloseCommand={settings?.youtrackCloseCommand ?? ''}
        initialYoutrackTokenSaved={settings?.youtrackTokenSaved ?? false}
        profiles={profiles}
        youtrackProjects={youtrackProjects}
        onListYouTrackProjects={listYouTrackProjects}
        onSubmit={saveSettings}
        onCancel={settings?.canCancel ? dismissSettings : undefined}
      />
      <ToastStack toasts={toasts} onDismiss={dismissToast} onOpenUrl={openUrl} />
    </div>
  )
}
