import { useCallback, useEffect, useRef, useState } from "react"
import type {
  AppConfig,
  ChatLaunchState,
  DesktopApi,
  ExpertDefinition,
  GatewayHealth,
  GatewayStatus,
  RuntimeCloudStatus,
  RuntimeStatus,
  RuntimeTelemetryStatus,
  SystemSummary,
  WorkspaceSummary
} from "@qjclaw/shared-types"

type ViewMode = "chat" | "experts" | "plugins" | "settings" | "knowledge"

interface BootstrapSkill {
  id: string
}

interface UseAppBootstrapOptions {
  desktopApi: DesktopApi
  selectedSkillId: string
  defaultSkill: BootstrapSkill
  viewMode: ViewMode
  startupCopy: {
    startingHint: string
    chatNotReadyError: string
    startupBooting: string
  }
  setSelectedSkillId(skillId: string): void
  setErrorText(value: string): void
  normalizeError(value: unknown): string
}

export function canExchangeMessages(
  workspace: WorkspaceSummary | null,
  runtimeStatus: RuntimeStatus | null,
  gatewayStatus: GatewayStatus | null
) {
  if (!workspace?.chatReady || !runtimeStatus || gatewayStatus?.state !== "connected") {
    return false
  }
  return runtimeStatus.activeMode === "external-gateway" || runtimeStatus.processState === "running"
}

export function useAppBootstrap({
  desktopApi,
  selectedSkillId,
  defaultSkill,
  viewMode,
  startupCopy,
  setSelectedSkillId,
  setErrorText,
  normalizeError
}: UseAppBootstrapOptions) {
  const [config, setConfig] = useState<AppConfig | null>(null)
  const [workspace, setWorkspace] = useState<WorkspaceSummary | null>(null)
  const [runtimeStatus, setRuntimeStatus] = useState<RuntimeStatus | null>(null)
  const [runtimeCloudStatus, setRuntimeCloudStatus] = useState<RuntimeCloudStatus | null>(null)
  const [runtimeTelemetry, setRuntimeTelemetry] = useState<RuntimeTelemetryStatus | null>(null)
  const [systemSummary, setSystemSummary] = useState<SystemSummary | null>(null)
  const [expertDefinitions, setExpertDefinitions] = useState<ExpertDefinition[]>([])
  const [gatewayStatus, setGatewayStatus] = useState<GatewayStatus | null>(null)
  const [gatewayHealth, setGatewayHealth] = useState<GatewayHealth | null>(null)
  const [refreshing, setRefreshing] = useState(false)
  const startupWarmupRequestedRef = useRef(false)
  const selectedSkillIdRef = useRef(selectedSkillId)

  useEffect(() => {
    selectedSkillIdRef.current = selectedSkillId
  }, [selectedSkillId])

  const refresh = useCallback(async (clearError = true): Promise<WorkspaceSummary | null> => {
    setRefreshing(true)
    if (clearError) {
      setErrorText("")
    }

    try {
      const [nextConfig, nextRuntime, nextCloud, nextTelemetry, nextSystem, nextWorkspace, nextExperts] = await Promise.all([
        desktopApi.config.load(),
        desktopApi.runtime.getStatus(),
        desktopApi.runtimeCloud.getStatus(),
        desktopApi.runtimeTelemetry.getStatus(),
        desktopApi.system.getSummary(),
        desktopApi.workspace.getSummary(),
        desktopApi.experts.list().catch(() => [])
      ])

      const statusResult = nextWorkspace.apiKeyConfigured
        ? await desktopApi.gateway.status().catch(() => null)
        : null

      setConfig(nextConfig)
      setWorkspace(nextWorkspace)
      setRuntimeStatus(nextRuntime)
      setRuntimeCloudStatus(nextCloud)
      setRuntimeTelemetry(nextTelemetry)
      setSystemSummary(nextSystem)
      setExpertDefinitions(nextExperts)
      setGatewayStatus(statusResult)

      const nextReadySkills = nextWorkspace.skills.filter((skill) => skill.ready)
      const nextSkills = nextReadySkills.length ? [defaultSkill, ...nextReadySkills] : [defaultSkill]
      if (!nextSkills.some((skill) => skill.id === selectedSkillIdRef.current)) {
        setSelectedSkillId(nextSkills[0].id)
      }

      if (canExchangeMessages(nextWorkspace, nextRuntime, statusResult)) {
        setGatewayHealth(await desktopApi.gateway.health().catch(() => null))
      } else {
        setGatewayHealth(null)
      }

      return nextWorkspace
    } catch (error) {
      setErrorText(normalizeError(error))
      return null
    } finally {
      setRefreshing(false)
    }
  }, [defaultSkill, desktopApi, normalizeError, setErrorText, setSelectedSkillId])

  const shellReady = workspace?.shellReady ?? false
  const bindingRequired = workspace?.bindingRequired ?? !Boolean(workspace?.apiKeyConfigured ?? config?.apiKeyConfigured)
  const isBound = !bindingRequired
  const chatLaunchState: ChatLaunchState = workspace?.chatLaunchState ?? (!bindingRequired ? "starting" : "unbound")
  const chatStatusMessage = workspace?.chatStatusMessage
    ?? (chatLaunchState === "starting" ? startupCopy.startingHint : chatLaunchState === "error" ? startupCopy.chatNotReadyError : "")
  const startupMessage = workspace?.startupMessage ?? ((refreshing && !workspace) ? startupCopy.startupBooting : chatStatusMessage)
  const startupPhase = workspace?.startupPhase ?? ((refreshing && !workspace) ? "syncing-config" : "idle")
  const startupStateActive = viewMode !== "settings" && ((refreshing && !workspace) || !shellReady || (isBound && chatLaunchState !== "ready"))

  useEffect(() => {
    void refresh()
  }, [refresh])

  useEffect(() => {
    const shouldPollStartupState = viewMode !== "settings"
      && startupStateActive
      && (chatLaunchState === "starting" || (!isBound && !shellReady))
    if (!shouldPollStartupState) {
      return
    }

    let cancelled = false
    let timer: number | undefined

    const pollWorkspace = async () => {
      const nextWorkspace = await refresh(false)
      if (cancelled) {
        return
      }

      const nextShouldPoll = nextWorkspace != null && (
        nextWorkspace.chatLaunchState === "starting"
        || (!nextWorkspace.shellReady && nextWorkspace.bindingRequired)
      )
      if (nextShouldPoll) {
        timer = window.setTimeout(() => {
          void pollWorkspace()
        }, 1000)
      }
    }

    timer = window.setTimeout(() => {
      void pollWorkspace()
    }, 800)

    return () => {
      cancelled = true
      if (typeof timer !== "undefined") {
        window.clearTimeout(timer)
      }
    }
  }, [chatLaunchState, isBound, refresh, shellReady, startupStateActive, viewMode])

  useEffect(() => {
    const shouldRequestStartupWarmup = startupStateActive && (
      (isBound && chatLaunchState === "starting")
      || (!isBound && !shellReady)
    )
    if (!shouldRequestStartupWarmup) {
      startupWarmupRequestedRef.current = false
      return
    }

    if (startupWarmupRequestedRef.current) {
      return
    }

    startupWarmupRequestedRef.current = true
    void desktopApi.workspace.warmup().catch(() => undefined)
  }, [chatLaunchState, desktopApi, isBound, shellReady, startupStateActive])

  return {
    config,
    setConfig,
    workspace,
    setWorkspace,
    runtimeStatus,
    setRuntimeStatus,
    runtimeCloudStatus,
    runtimeTelemetry,
    setRuntimeTelemetry,
    systemSummary,
    expertDefinitions,
    gatewayStatus,
    setGatewayStatus,
    gatewayHealth,
    refreshing,
    refresh,
    shellReady,
    bindingRequired,
    isBound,
    chatLaunchState,
    chatStatusMessage,
    startupMessage,
    startupPhase,
    startupStateActive
  }
}
