import { useCallback, useRef, useState } from 'react'
import { ConfigProvider, Message, Spin } from '@arco-design/web-react'
import '@arco-design/web-react/dist/css/arco.css'
import '../App.css'
import { ApiError, login, setApiBaseUrl, type ApiSession } from '../api/client'
import { createAISession, listAISessions, type CreateAISessionInput } from '../api/ai'
import { analyzeInboxItem, confirmInboxItem } from '../api/inbox'
import { mapWorkspace } from '../api/mappers'
import {
  createCronPlan,
  createProject,
  createProjectTag,
  createTask,
  fetchProjectWorkspace,
  fetchProjects,
  updateProject,
  updateTask,
  uploadSource,
} from '../api/projects'
import type { SearchResultDTO } from '../api/search'
import { LoginPage } from '../pages/login'
import { ProjectActionModals, type CronDraft, type ProjectActionModal, type ProjectDraft, type SourceDraft, type TaskDraft } from '../pages/projects/project-action-modals'
import { SearchResultPreview } from '../pages/projects/search-result-preview'
import type { ProjectSettingsUpdate } from '../pages/projects/project-sidebar'
import { ProjectPage } from '../pages/workspace-home'
import type { WorkspaceTaskUpdate } from '../pages/workspace-body'
import type { ChannelKey, Project, ProjectWorkspace, Screen, Theme, WorkbenchView } from '../pages/projects/project-types'
import { useWorkbenchSearch } from './use-workbench-search'

function App() {
  const [screen, setScreen] = useState<Screen>('login')
  const [theme, setTheme] = useState<Theme>('light')
  const [activeView, setActiveView] = useState<WorkbenchView>('workspace')
  const [session, setSession] = useState<ApiSession | null>(null)
  const [workspaces, setWorkspaces] = useState<ProjectWorkspace[]>([])
  const [activeProjectID, setActiveProjectID] = useState<string>('')
  const activeProjectIDRef = useRef('')
  const [activeChannel, setActiveChannel] = useState<ChannelKey>('overview')
  const [activeTaskID, setActiveTaskID] = useState<string | null>(null)
  const [, setSelectedItem] = useState('探索采集')
  const [loading, setLoading] = useState(false)
  const [actionLoading, setActionLoading] = useState(false)
  const [activeModal, setActiveModal] = useState<ProjectActionModal>(null)
  const [searchResultPreview, setSearchResultPreview] = useState<SearchResultDTO | null>(null)
  const workspaceSearch = useWorkbenchSearch(session)

  const handleListAISessions = useCallback((projectId: string, signal?: AbortSignal) => {
    if (!session) return Promise.reject(new Error('未登录'))
    return listAISessions(session, projectId, signal)
  }, [session])

  const handleCreateAISession = useCallback((projectId: string, input: CreateAISessionInput, signal?: AbortSignal) => {
    if (!session) return Promise.reject(new Error('未登录'))
    return createAISession(session, projectId, input, signal)
  }, [session])

  const dark = theme === 'dark'
  const activeWorkspace = workspaces.find((workspace) => workspace.project.id === activeProjectID) ?? workspaces[0]
  const activeTagOptions = activeWorkspace?.tags.filter((tag) => tag !== 'all' && tag !== '全部') ?? []

  function selectActiveProject(projectID: string) {
    activeProjectIDRef.current = projectID
    setActiveProjectID(projectID)
  }

  async function loadWorkspaces(nextSession: ApiSession, preferredProjectID?: string) {
    const projectIDWhenStarted = activeProjectIDRef.current
    const backendProjects = await fetchProjects(nextSession)
    const backendWorkspaces = await Promise.all(
      backendProjects.map((project, index) => fetchProjectWorkspace(nextSession, project.id).then((workspace) => mapWorkspace(workspace, index))),
    )
    setWorkspaces(backendWorkspaces)
    const latestProjectID = activeProjectIDRef.current
    const requestedProjectID = latestProjectID !== projectIDWhenStarted
      ? latestProjectID
      : preferredProjectID ?? latestProjectID
    const nextProjectID = backendWorkspaces.find((workspace) => workspace.project.id === requestedProjectID)?.project.id ?? backendWorkspaces[0]?.project.id ?? ''
    selectActiveProject(nextProjectID)
    return backendWorkspaces
  }

  async function handleLogin(input: { server: string; email: string; password: string }) {
    setLoading(true)
    try {
      setApiBaseUrl(input.server)
      const nextSession = await login(input.email, input.password)
      const backendWorkspaces = await loadWorkspaces(nextSession, '')
      setSession(nextSession)
      selectActiveProject(backendWorkspaces[0]?.project.id ?? '')
      setActiveChannel('overview')
      setActiveView('workspace')
      setScreen('workbench')
    } catch (error) {
      Message.error(error instanceof Error ? error.message : '登录失败')
    } finally {
      setLoading(false)
    }
  }

  async function refreshAfterAction(nextProjectID?: string) {
    if (!session) return
    await loadWorkspaces(session, nextProjectID)
  }

  async function runAction(action: () => Promise<void>, success: string) {
    setActionLoading(true)
    try {
      await action()
      setActiveModal(null)
      Message.success(success)
    } catch (error) {
      Message.error(error instanceof Error ? error.message : '操作失败')
    } finally {
      setActionLoading(false)
    }
  }

  function requireSession() {
    if (!session) throw new Error('未登录')
    return session
  }

  function requireActiveProject() {
    if (!activeWorkspace?.project.id) throw new Error('未选择项目')
    return activeWorkspace.project.id
  }

  function normalizeOptionalTime(value: string) {
    const trimmed = value.trim()
    return trimmed === '' ? undefined : trimmed
  }

  function handleCreateProject(draft: ProjectDraft) {
    if (!draft.name.trim()) {
      Message.warning('请输入项目名称')
      return
    }
    void runAction(async () => {
      const created = await createProject(requireSession(), {
        name: draft.name.trim(),
        identifier: draft.identifier.trim(),
        icon: draft.icon.trim(),
        background: draft.background.trim(),
        description: draft.description.trim(),
      })
      const nextProjectID = created.id
      await refreshAfterAction(nextProjectID)
      if (nextProjectID) {
        selectActiveProject(nextProjectID)
        setActiveView('project')
        setActiveChannel('overview')
      }
    }, '项目已创建')
  }

  function handleCreateTask(draft: TaskDraft) {
    if (!draft.title.trim()) {
      Message.warning('请输入任务标题')
      return
    }
    void runAction(async () => {
      await createTask(requireSession(), requireActiveProject(), {
        title: draft.title.trim(),
        description: draft.description.trim(),
        status: 'open',
        tag: draft.tag.trim(),
      })
      await refreshAfterAction()
      setActiveChannel('tasks')
    }, '任务已创建')
  }

  function handleUploadSource(draft: SourceDraft) {
    if (!draft.file) {
      Message.warning('请选择文件')
      return
    }
    const file = draft.file
    void runAction(async () => {
      await uploadSource(requireSession(), requireActiveProject(), {
        title: draft.title.trim(),
        file,
      })
      await refreshAfterAction()
      setActiveChannel('notes')
    }, '文件已上传')
  }

  function handleCreateCronPlan(draft: CronDraft) {
    if (!draft.title.trim()) {
      Message.warning('请输入计划名称')
      return
    }
    if (!draft.schedule.trim()) {
      Message.warning('请输入 Cron 表达式')
      return
    }
    void runAction(async () => {
      await createCronPlan(requireSession(), requireActiveProject(), {
        title: draft.title.trim(),
        schedule: draft.schedule.trim(),
        enabled: draft.enabled,
        nextRunAt: normalizeOptionalTime(draft.nextRunAt),
      })
      await refreshAfterAction()
      setActiveChannel('cron')
    }, '计划任务已创建')
  }

  function handleCreateProjectTag(name: string) {
    const trimmedName = name.trim()
    if (!trimmedName) {
      Message.warning('请输入标签名称')
      return
    }
    void runAction(async () => {
      await createProjectTag(requireSession(), requireActiveProject(), { name: trimmedName })
      await refreshAfterAction()
    }, '标签已创建')
  }

  function openTask(project: Project, taskID: string) {
    selectActiveProject(project.id)
    setActiveView('project')
    setActiveChannel('tasks')
    setActiveTaskID(taskID)
  }

  function handleUpdateWorkspaceTask(update: WorkspaceTaskUpdate) {
    void runAction(async () => {
      await updateTask(requireSession(), update.originalProjectId, update.taskId, {
        title: update.title,
        description: update.summary,
        completed: update.completed,
        nextProjectId: update.nextProjectId,
        tag: update.tag,
      })
      await refreshAfterAction(update.nextProjectId)
    }, '任务已更新')
  }

  async function handleUpdateProject(update: ProjectSettingsUpdate) {
    const currentSession = requireSession()
    await updateProject(currentSession, update.projectId, {
      name: update.name,
      identifier: update.identifier,
      icon: update.icon,
      background: update.background,
      description: update.description,
    })
    await loadWorkspaces(currentSession, update.projectId)
    Message.success('项目设置已更新')
  }

  async function handleAnalyzeInbox(inboxId: string) {
    const response = await analyzeInboxItem(requireSession(), inboxId)
    return response.suggestions
  }

  async function handleConfirmInbox(inboxId: string, suggestionIds: string[]) {
    const confirmationProjectID = activeProjectIDRef.current
    let response
    try {
      response = await confirmInboxItem(requireSession(), inboxId, suggestionIds)
    } catch (error) {
      if (!(error instanceof ApiError) || error.status !== 409) throw error
      if (activeProjectIDRef.current !== confirmationProjectID) {
        throw new ApiError(409, 'conflict', '确认状态发生冲突，请重新核对当前工作区')
      }
      try {
        await refreshAfterAction()
      } catch {
        throw new ApiError(409, 'conflict', '确认状态发生冲突，请重新进入项目核对')
      }
      throw new ApiError(409, 'conflict', '确认状态发生冲突，工作区已刷新，请重新核对')
    }
    if (activeProjectIDRef.current !== confirmationProjectID) {
      return { createdCount: response.createdCount }
    }
    try {
      await refreshAfterAction()
      return { createdCount: response.createdCount }
    } catch {
      return {
        createdCount: response.createdCount,
        refreshError: '对象已创建，但工作区刷新失败，请稍后重新进入项目',
      }
    }
  }

  function handleSelectSearchResult(result: SearchResultDTO) {
    const target = searchResultTarget(result.type)
    if (!target) {
      Message.warning('该搜索结果暂不支持导航')
      return
    }
    const owningWorkspace = workspaces.find((workspace) => workspace.project.id === result.projectId)
    if (!owningWorkspace) {
      if (isAuthorizedExternalPreview(result)) {
        setSearchResultPreview(result)
      } else {
        Message.warning('该搜索结果所在项目当前不可访问')
      }
      return
    }
    selectActiveProject(owningWorkspace.project.id)
    setActiveView('project')
    setActiveChannel(target.channel)
    setActiveTaskID(target.openTask ? result.id : null)
    if (result.type === 'note') setSelectedItem(result.title)
  }

  return (
    <ConfigProvider>
      <main className={dark ? 'app theme-dark' : 'app'}>
        {screen === 'login' ? (
          <Spin loading={loading} style={{ width: '100%' }}>
            <LoginPage onLogin={handleLogin} />
          </Spin>
        ) : activeWorkspace && session ? (
          <ProjectPage
            activeView={activeView}
            activeWorkspace={activeWorkspace}
            workspaces={workspaces}
            activeChannel={activeChannel}
            activeTaskID={activeTaskID}
            theme={theme}
            onSelectWorkspace={() => setActiveView('workspace')}
            onSelectWorkspaceExplore={() => setActiveView('workspace-explore')}
            onSelectProject={(project) => {
              selectActiveProject(project.id)
              setActiveView('project')
              setActiveChannel('overview')
              setActiveTaskID(null)
            }}
            onSelectChannel={(channel) => {
              setActiveChannel(channel)
              setActiveTaskID(null)
            }}
            onSelectItem={setSelectedItem}
            onOpenTask={openTask}
            onCloseTask={() => setActiveTaskID(null)}
            onToggleTheme={() => setTheme(dark ? 'light' : 'dark')}
            onCreateProject={() => setActiveModal('project')}
            onCreateTask={() => setActiveModal('task')}
            onUploadSource={() => setActiveModal('source')}
            onCreateCronPlan={() => setActiveModal('cron')}
            onUpdateWorkspaceTask={handleUpdateWorkspaceTask}
            onUpdateProject={handleUpdateProject}
            onCreateProjectTag={handleCreateProjectTag}
            searchQuery={workspaceSearch.query}
            searchLoading={workspaceSearch.loading}
            searchSearched={workspaceSearch.searched}
            searchResults={workspaceSearch.results}
            onSearchQueryChange={workspaceSearch.onQueryChange}
            onSearch={() => void workspaceSearch.onSearch()}
            onSelectSearchResult={handleSelectSearchResult}
            onAnalyzeInbox={handleAnalyzeInbox}
            onConfirmInbox={handleConfirmInbox}
            onListAISessions={handleListAISessions}
            onCreateAISession={handleCreateAISession}
          />
        ) : (
          <Spin loading />
        )}
        <ProjectActionModals
          activeModal={activeModal}
          loading={actionLoading}
          onClose={() => setActiveModal(null)}
          onCreateProject={handleCreateProject}
          onCreateTask={handleCreateTask}
          onUploadSource={handleUploadSource}
          onCreateCronPlan={handleCreateCronPlan}
          tagOptions={activeTagOptions}
        />
        <SearchResultPreview result={searchResultPreview} onClose={() => setSearchResultPreview(null)} />
      </main>
    </ConfigProvider>
  )
}

function searchResultTarget(type: string): { channel: ChannelKey; openTask: boolean } | null {
  if (type === 'project') return { channel: 'overview', openTask: false }
  if (type === 'task') return { channel: 'tasks', openTask: true }
  if (type === 'note') return { channel: 'notes', openTask: false }
  return null
}

function isAuthorizedExternalPreview(result: SearchResultDTO) {
  return (
    (result.type === 'task' || result.type === 'note')
    && typeof result.projectId === 'string'
    && result.projectId.trim() !== ''
    && typeof result.title === 'string'
    && result.title.trim() !== ''
    && typeof result.snippet === 'string'
  )
}

export default App
