import { useMemo } from "react"
import type { AppConfig, ChatAttachment, DesktopApi, ExpertDefinition, ProjectSessionSummary, SaveConfigInput, WorkspaceSummary } from "@qjclaw/shared-types"
import { HOME_CHAT_PROJECT_ID } from "../../lib/constants"
import type { SubmitPromptOptions } from "../chat/useChatStreamingController"
import { waitForSmokeUiReady } from "./useSmokeActions"

type ViewMode = "chat" | "experts" | "plugins" | "settings" | "knowledge"
type ExpertProject = WorkspaceSummary["projects"][number]
type SidebarExpertEntry = {
  definition: ExpertDefinition
  project?: ExpertProject
  displayName: string
  isAvailable: boolean
}

interface UseSmokeActionHandlersDeps {
  activeSessionId: string
  composerAttachments: ChatAttachment[]
  desktopApi: DesktopApi
  expertDefinitions: ExpertDefinition[]
  prompt: string
  sessionScopeProjectId?: string
  workspace: WorkspaceSummary | null
  selectedSkillId: string
  defaultSkillId: string
  setViewMode: (viewMode: ViewMode | ((current: ViewMode) => ViewMode)) => void
  handleNavSelection: (viewMode: ViewMode) => void | Promise<void>
  setSelectedSkillId: (skillId: string) => void
  setActiveProjectSession: (sessionId: string) => void
  setPrompt: (prompt: string) => void
  setPendingDrafts: {
    setWorkspacePathDraft: (value: string) => void
    setLobsterKeyDraft: (value: string) => void
    setImageModelApiKeyDraft: (value: string) => void
    setVideoModelApiKeyDraft: (value: string) => void
    setCopywritingModelApiKeyDraft: (value: string) => void
    setDigitalHumanVolcAccessKeyDraft: (value: string) => void
    setDigitalHumanVolcSecretKeyDraft: (value: string) => void
    setDigitalHumanQiniuAccessKeyDraft: (value: string) => void
    setDigitalHumanQiniuSecretKeyDraft: (value: string) => void
    setVideoAnalyzerBaseUrlDraft: (value: string) => void
    setVideoAnalyzerModelIdDraft: (value: string) => void
    setVideoAnalyzerApiKeyDraft: (value: string) => void
    setReplicationBriefBaseUrlDraft: (value: string) => void
    setReplicationBriefModelIdDraft: (value: string) => void
    setReplicationBriefApiKeyDraft: (value: string) => void
    setVectcutBaseUrlDraft: (value: string) => void
    setVectcutFileBaseUrlDraft: (value: string) => void
    setVectcutApiKeyDraft: (value: string) => void
    setXhsFeishuAppIdDraft: (value: string) => void
    setXhsFeishuAppSecretDraft: (value: string) => void
    setXhsFeishuAppTokenDraft: (value: string) => void
    setXhsFeishuTableIdDraft: (value: string) => void
  }
  setConfig: (config: AppConfig) => void
  upsertSession: (session: ProjectSessionSummary) => void
  submitPrompt: (options: SubmitPromptOptions) => Promise<void>
  switchExpert: (projectId: string) => Promise<void>
  activateHomeShortcut: (entry: SidebarExpertEntry) => Promise<WorkspaceSummary | null>
  buildShortcutPrompt: (definition: ExpertDefinition) => string
  resolveExpertProject: (projects: ExpertProject[], definition: ExpertDefinition) => ExpertProject | undefined
  resolveExpertDisplayName: (definition: ExpertDefinition, project?: ExpertProject) => string
  saveConfig: (options?: {
    lobsterKey?: string
    workspacePath?: string
    expertModelConfig?: SaveConfigInput["expertModelConfig"]
    douyinRuntimeConfig?: SaveConfigInput["douyinRuntimeConfig"]
    xhsFeishuConfig?: SaveConfigInput["xhsFeishuConfig"]
    successMessage?: string
  }) => Promise<void>
  waitForSmokeConfigPublish: (expected: AppConfig["expertModelConfig"]) => Promise<void>
  resolveHomeIntentSuggestion: (prompt?: string) => Promise<{
    visible: boolean
    projectId?: string
    projectName?: string
    pendingPrompt?: string
  }>
  continueHomeIntentSuggestion: (options?: { createSmokeSession?: boolean }) => Promise<{
    continued: boolean
    sessionId?: string
  }>
  switchHomeIntentSuggestion: (options?: { smokeTitle?: string; throwOnError?: boolean }) => Promise<{
    switched: boolean
    projectId?: string
    sessionId?: string
  }>
  dismissHomeIntentSuggestion: () => void
}

function normalizeSmokeAttachments(attachments: ChatAttachment[] | undefined): ChatAttachment[] | undefined {
  if (!Array.isArray(attachments)) {
    return undefined
  }

  return attachments.filter((attachment): attachment is ChatAttachment =>
    Boolean(
      attachment
      && (attachment.kind === "image" || attachment.kind === "file")
      && attachment.localPath?.trim()
    ))
}

export function useSmokeActionHandlers(deps: UseSmokeActionHandlersDeps): NonNullable<typeof window.__QJC_SMOKE_ACTIONS__> {
  const {
    activeSessionId,
    composerAttachments,
    desktopApi,
    expertDefinitions,
    prompt,
    sessionScopeProjectId,
    workspace,
    selectedSkillId,
    defaultSkillId,
    setViewMode,
    handleNavSelection,
    setSelectedSkillId,
    setActiveProjectSession,
    setPrompt,
    setPendingDrafts,
    setConfig,
    upsertSession,
    submitPrompt,
    switchExpert,
    activateHomeShortcut,
    buildShortcutPrompt,
    resolveExpertProject,
    resolveExpertDisplayName,
    saveConfig,
    waitForSmokeConfigPublish,
    resolveHomeIntentSuggestion,
    continueHomeIntentSuggestion,
    switchHomeIntentSuggestion,
    dismissHomeIntentSuggestion
  } = deps

  return useMemo<NonNullable<typeof window.__QJC_SMOKE_ACTIONS__>>(() => ({
    sendConversationPrompt: async (nextPrompt, options) => {
      const mode = options?.mode === "experts" ? "experts" : "chat"
      const requestedProjectId = options?.projectId?.trim()
      const resolvedSkillId = options?.skillId?.trim() || undefined
      let resolvedSessionId = options?.sessionId?.trim() || ""
      const resolvedAttachments = normalizeSmokeAttachments(options?.attachments)

      setViewMode(mode)
      if (mode === "experts" && requestedProjectId) {
        await switchExpert(requestedProjectId)
        const projectSessions = await desktopApi.chat.listSessionsByProject(requestedProjectId)
        resolvedSessionId = resolvedSessionId
          || projectSessions.find((session) => session.id === activeSessionId && session.projectId === requestedProjectId)?.id
          || projectSessions[0]?.id
          || (await desktopApi.chat.createSessionForProject(requestedProjectId, "Smoke Test")).id
      } else if (resolvedSessionId) {
        setActiveProjectSession(resolvedSessionId)
      }

      setSelectedSkillId(resolvedSkillId ?? defaultSkillId)
      if (resolvedSessionId) {
        setActiveProjectSession(resolvedSessionId)
      }
      setPrompt(nextPrompt)
      await waitForSmokeUiReady(mode)
      await submitPrompt({
        promptText: nextPrompt,
        skillId: resolvedSkillId,
        forcedSessionId: resolvedSessionId || undefined,
        forcedProjectId: requestedProjectId || undefined,
        forcedAttachments: resolvedAttachments
      })

      return {
        mode,
        projectId: mode === "experts" ? requestedProjectId : undefined,
        sessionId: resolvedSessionId,
        skillId: resolvedSkillId
      }
    },
    runSessionSwitchStreamScenario: async (nextPrompt: string) => {
      setViewMode("chat")
      setSelectedSkillId(defaultSkillId)
      const streamSession = await desktopApi.chat.createSessionForProject(HOME_CHAT_PROJECT_ID, "Smoke Stream Session")
      const alternateSession = await desktopApi.chat.createSessionForProject(HOME_CHAT_PROJECT_ID, "Smoke Alternate Session")
      upsertSession(streamSession)
      upsertSession(alternateSession)
      setActiveProjectSession(streamSession.id)
      setPrompt(nextPrompt)
      await waitForSmokeUiReady("chat")
      await submitPrompt({
        promptText: nextPrompt,
        forcedSessionId: streamSession.id,
        forcedProjectId: HOME_CHAT_PROJECT_ID
      })

      await new Promise<void>((resolve) => window.setTimeout(resolve, 350))
      const phaseBeforeSwitch = window.__QJC_SMOKE__?.streamSmoke?.phase
      setActiveProjectSession(alternateSession.id)
      await new Promise<void>((resolve) => window.setTimeout(resolve, 150))
      const switchedAwayActiveSessionId = window.__QJC_SMOKE__?.activeSessionId || alternateSession.id
      setActiveProjectSession(streamSession.id)
      await new Promise<void>((resolve) => window.setTimeout(resolve, 150))
      const latestState = window.__QJC_SMOKE__
      const phaseAtReturn = latestState?.streamSmoke?.phase
      const completedEventCountAtReturn = Number(latestState?.streamSmoke?.completedEventCount || 0)
      const visibleMessagesAfterReturn = latestState?.messages ?? []
      const cachedMessagesAfterReturn = latestState?.messagesBySession?.[streamSession.id] ?? visibleMessagesAfterReturn

      return {
        smokeScenario: "session-switch-stream" as const,
        sessionId: streamSession.id,
        scenarioResult: {
          streamSessionId: streamSession.id,
          alternateSessionId: alternateSession.id,
          switchedAwayActiveSessionId,
          returnedActiveSessionId: window.__QJC_SMOKE__?.activeSessionId || streamSession.id,
          phaseBeforeSwitch,
          phaseAtReturn,
          completedEventCountAtReturn,
          visibleMessagesAfterReturn,
          cachedMessagesAfterReturn
        }
      }
    },
    activateExpertEntry: async (expertId) => {
      const normalizedExpertId = expertId.trim()
      const latestExperts = await desktopApi.experts.list().catch(() => expertDefinitions)
      const latestWorkspace = await desktopApi.workspace.getSummary().catch(() => workspace)
      const latestVisibleProjects = (latestWorkspace?.projects ?? []).filter((project) => !project.isBuiltinHome)
      const latestStandaloneEntries = latestExperts
        .filter((definition) => definition.entryMode === "standalone")
        .map((definition) => {
          const project = resolveExpertProject(latestVisibleProjects, definition)
          return {
            definition,
            project,
            displayName: resolveExpertDisplayName(definition, project),
            isAvailable: Boolean(project && definition.promptAvailable)
          } satisfies SidebarExpertEntry
        })
      const latestHomeShortcutEntries = latestExperts
        .filter((definition) => definition.entryMode === "home-chat-shortcut")
        .map((definition) => ({
          definition,
          project: undefined,
          displayName: definition.name,
          isAvailable: definition.promptAvailable
        } satisfies SidebarExpertEntry))

      const standaloneEntry = latestStandaloneEntries.find((entry) => entry.definition.id === normalizedExpertId)
      if (standaloneEntry?.project) {
        await switchExpert(standaloneEntry.project.id)
        return {
          expertId: normalizedExpertId,
          entryMode: standaloneEntry.definition.entryMode,
          viewMode: "experts" as const,
          currentProjectId: standaloneEntry.project.id,
          prompt
        }
      }

      const shortcutEntry = latestHomeShortcutEntries.find((entry) => entry.definition.id === normalizedExpertId)
      if (shortcutEntry) {
        const nextWorkspace = await activateHomeShortcut(shortcutEntry)
        return {
          expertId: normalizedExpertId,
          entryMode: shortcutEntry.definition.entryMode,
          viewMode: "chat" as const,
          currentProjectId: nextWorkspace?.currentProjectId,
          sessionScopeProjectId: HOME_CHAT_PROJECT_ID,
          prompt: buildShortcutPrompt(shortcutEntry.definition)
        }
      }

      throw new Error(`Unknown smoke expert entry: ${normalizedExpertId}`)
    },
    navigateToView: async (nextView) => {
      await handleNavSelection(nextView)
      await waitForSmokeUiReady(nextView)
      return { view: nextView }
    },
    runHomeChatNavigationScenario: async (sourceView = "settings") => {
      const resolvedSourceView = sourceView
      setViewMode(resolvedSourceView)
      await waitForSmokeUiReady(resolvedSourceView)
      await handleNavSelection("chat")
      await waitForSmokeUiReady("chat")
      const started = Date.now()
      while (Date.now() - started < 10000) {
        const state = window.__QJC_SMOKE__
        const activeSessionId = state?.activeSessionId ?? ""
        const activeSession = state?.sessions.find((session) => session.id === activeSessionId)
        if (
          state?.viewMode === "chat"
          && state.ui?.sessionScopeProjectId === HOME_CHAT_PROJECT_ID
          && activeSession?.projectId === HOME_CHAT_PROJECT_ID
        ) {
          return {
            sourceView: resolvedSourceView,
            viewMode: state.viewMode,
            sessionScopeProjectId: state.ui.sessionScopeProjectId,
            activeSessionId,
            activeSessionProjectId: activeSession.projectId,
            messageCount: state.messages.length
          }
        }
        await new Promise<void>((resolve) => window.setTimeout(resolve, 50))
      }

      const state = window.__QJC_SMOKE__
      throw new Error(
        "Home chat navigation did not settle on a home session: "
        + JSON.stringify({
          viewMode: state?.viewMode,
          sessionScopeProjectId: state?.ui?.sessionScopeProjectId,
          activeSessionId: state?.activeSessionId,
          sessionProjectIds: state?.sessions.map((session) => session.projectId)
        })
      )
    },
    saveSettingsConfig: async (options) => {
      const nextWorkspacePath = options?.workspacePath
      const nextExpertModelConfig = options?.expertModelConfig
      const nextDouyinRuntimeConfig = options?.douyinRuntimeConfig
      const nextXhsFeishuConfig = options?.xhsFeishuConfig
      if (typeof nextWorkspacePath === "string") {
        setPendingDrafts.setWorkspacePathDraft(nextWorkspacePath)
      }
      if (nextExpertModelConfig?.image) {
        setPendingDrafts.setImageModelApiKeyDraft(nextExpertModelConfig.image.apiKey ?? "")
      }
      if (nextExpertModelConfig?.video) {
        setPendingDrafts.setVideoModelApiKeyDraft(nextExpertModelConfig.video.apiKey ?? "")
      }
      if (nextExpertModelConfig?.copywriting) {
        setPendingDrafts.setCopywritingModelApiKeyDraft(nextExpertModelConfig.copywriting.apiKey ?? "")
      }
      if (nextExpertModelConfig?.digitalHuman) {
        setPendingDrafts.setDigitalHumanVolcAccessKeyDraft(nextExpertModelConfig.digitalHuman.volcAccessKey ?? "")
        setPendingDrafts.setDigitalHumanVolcSecretKeyDraft(nextExpertModelConfig.digitalHuman.volcSecretKey ?? "")
        setPendingDrafts.setDigitalHumanQiniuAccessKeyDraft(nextExpertModelConfig.digitalHuman.qiniuAccessKey ?? "")
        setPendingDrafts.setDigitalHumanQiniuSecretKeyDraft(nextExpertModelConfig.digitalHuman.qiniuSecretKey ?? "")
      }
      if (nextDouyinRuntimeConfig?.videoAnalyzer) {
        setPendingDrafts.setVideoAnalyzerBaseUrlDraft(nextDouyinRuntimeConfig.videoAnalyzer.baseUrl ?? "")
        setPendingDrafts.setVideoAnalyzerModelIdDraft(nextDouyinRuntimeConfig.videoAnalyzer.modelId ?? "")
        setPendingDrafts.setVideoAnalyzerApiKeyDraft(nextDouyinRuntimeConfig.videoAnalyzer.apiKey ?? "")
      }
      if (nextDouyinRuntimeConfig?.replicationBrief) {
        setPendingDrafts.setReplicationBriefBaseUrlDraft(nextDouyinRuntimeConfig.replicationBrief.baseUrl ?? "")
        setPendingDrafts.setReplicationBriefModelIdDraft(nextDouyinRuntimeConfig.replicationBrief.modelId ?? "")
        setPendingDrafts.setReplicationBriefApiKeyDraft(nextDouyinRuntimeConfig.replicationBrief.apiKey ?? "")
      }
      if (nextDouyinRuntimeConfig?.vectcut) {
        setPendingDrafts.setVectcutBaseUrlDraft(nextDouyinRuntimeConfig.vectcut.baseUrl ?? "")
        setPendingDrafts.setVectcutFileBaseUrlDraft(nextDouyinRuntimeConfig.vectcut.fileBaseUrl ?? "")
        setPendingDrafts.setVectcutApiKeyDraft(nextDouyinRuntimeConfig.vectcut.apiKey ?? "")
      }
      if (nextXhsFeishuConfig) {
        setPendingDrafts.setXhsFeishuAppIdDraft(nextXhsFeishuConfig.appId ?? "")
        setPendingDrafts.setXhsFeishuAppSecretDraft(nextXhsFeishuConfig.appSecret ?? "")
        setPendingDrafts.setXhsFeishuAppTokenDraft(nextXhsFeishuConfig.appToken ?? "")
        setPendingDrafts.setXhsFeishuTableIdDraft(nextXhsFeishuConfig.tableId ?? "")
      }
      if (typeof options?.lobsterKey === "string") {
        setPendingDrafts.setLobsterKeyDraft(options.lobsterKey)
      }

      await saveConfig({
        lobsterKey: options?.lobsterKey,
        workspacePath: nextWorkspacePath,
        expertModelConfig: nextExpertModelConfig,
        douyinRuntimeConfig: nextDouyinRuntimeConfig,
        xhsFeishuConfig: nextXhsFeishuConfig
      })

      const latestConfig = await desktopApi.config.load()
      setConfig(latestConfig)
      setPendingDrafts.setWorkspacePathDraft(latestConfig.workspacePath)
      setPendingDrafts.setImageModelApiKeyDraft("")
      setPendingDrafts.setVideoModelApiKeyDraft("")
      setPendingDrafts.setCopywritingModelApiKeyDraft("")
      setPendingDrafts.setDigitalHumanVolcAccessKeyDraft("")
      setPendingDrafts.setDigitalHumanVolcSecretKeyDraft("")
      setPendingDrafts.setDigitalHumanQiniuAccessKeyDraft("")
      setPendingDrafts.setDigitalHumanQiniuSecretKeyDraft("")
      setPendingDrafts.setVideoAnalyzerApiKeyDraft("")
      setPendingDrafts.setReplicationBriefApiKeyDraft("")
      setPendingDrafts.setVectcutApiKeyDraft("")
      setPendingDrafts.setXhsFeishuAppIdDraft("")
      setPendingDrafts.setXhsFeishuAppSecretDraft("")
      setPendingDrafts.setXhsFeishuAppTokenDraft("")
      setPendingDrafts.setXhsFeishuTableIdDraft("")
      await waitForSmokeConfigPublish(latestConfig.expertModelConfig)

      return {
        workspacePath: latestConfig.workspacePath,
        expertModelConfig: latestConfig.expertModelConfig,
        douyinRuntimeConfig: latestConfig.douyinRuntimeConfig,
        xhsFeishuConfig: latestConfig.xhsFeishuConfig,
        apiKeyConfigured: latestConfig.apiKeyConfigured
      }
    },
    createProjectSession: async (projectId, title) => {
      const resolvedProjectId = projectId?.trim() || sessionScopeProjectId || HOME_CHAT_PROJECT_ID
      const session = await desktopApi.chat.createSessionForProject(resolvedProjectId, title)
      upsertSession(session)
      return session
    },
    openSession: async (sessionId) => {
      setViewMode((current) => (current === "experts" ? "experts" : "chat"))
      setActiveProjectSession(sessionId)
      return { sessionId }
    },
    resolveHomeIntentSuggestion,
    continueHomeIntentSuggestion: async () => {
      const result = await continueHomeIntentSuggestion({ createSmokeSession: true })
      return { continued: result.continued }
    },
    switchHomeIntentSuggestion: async () => {
      const result = await switchHomeIntentSuggestion({ smokeTitle: "Smoke Home Intent Expert", throwOnError: true })
      return { switched: result.switched, projectId: result.projectId }
    },
    dismissHomeIntentSuggestion: async () => {
      dismissHomeIntentSuggestion()
      return { dismissed: true }
    },
    clickWindowControl: async (kind) => {
      const selector = kind === "close"
        ? ".window-control-button.close"
        : `.window-control-button[aria-label="${kind === "minimize" ? "最小化窗口" : "最大化窗口"}"]`
      const button = document.querySelector<HTMLButtonElement>(selector)
      if (!button) {
        throw new Error(`Window control button not found for ${kind}.`)
      }
      button.click()
      return { kind }
    }
  }), [
    activeSessionId,
    activateHomeShortcut,
    buildShortcutPrompt,
    composerAttachments,
    continueHomeIntentSuggestion,
    defaultSkillId,
    desktopApi.chat,
    desktopApi.config,
    desktopApi.experts,
    desktopApi.workspace,
    dismissHomeIntentSuggestion,
    expertDefinitions,
    handleNavSelection,
    prompt,
    resolveExpertDisplayName,
    resolveExpertProject,
    resolveHomeIntentSuggestion,
    saveConfig,
    selectedSkillId,
    sessionScopeProjectId,
    setActiveProjectSession,
    setConfig,
    setPendingDrafts,
    setPrompt,
    setSelectedSkillId,
    setViewMode,
    submitPrompt,
    switchExpert,
    switchHomeIntentSuggestion,
    upsertSession,
    waitForSmokeConfigPublish,
    workspace
  ])
}
