import { useCallback, useEffect, useState } from "react"
import type { DesktopApi, WorkspaceSummary } from "@qjclaw/shared-types"
import { EMPTY_SESSION_ID, HOME_CHAT_PROJECT_ID } from "../../lib/constants"
import { resolvePreferredSessionId } from "../../lib/chat-utils"

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

interface UseChatSessionsControllerDeps {
  desktopApi: DesktopApi
  viewMode: ViewMode
  isBound: boolean
  bindingRequired: boolean
  sessionScopeProjectId?: string
  activeSessionId: string
  preferredSessionId?: string
  sendPhase: "idle" | "preparing" | "streaming" | "finalizing"
  activeStreamSessionId?: string
  workspace: WorkspaceSummary | null
  homeSessionTitle: string
  setSessions: (sessions: WorkspaceSummary["sessions"]) => void
  clearSessions: () => void
  clearAllSessionMessages: () => void
  setActiveProjectSession: (sessionId: string) => void
  upsertSession: (session: WorkspaceSummary["sessions"][number]) => void
  setWorkspace: (workspace: WorkspaceSummary | null) => void
  setErrorText: (value: string) => void
}

export function useChatSessionsController(deps: UseChatSessionsControllerDeps) {
  const {
    desktopApi,
    viewMode,
    isBound,
    bindingRequired,
    sessionScopeProjectId,
    activeSessionId,
    preferredSessionId,
    sendPhase,
    activeStreamSessionId,
    workspace,
    homeSessionTitle,
    setSessions,
    clearSessions,
    clearAllSessionMessages,
    setActiveProjectSession,
    upsertSession,
    setWorkspace,
    setErrorText
  } = deps

  const [projectActionPending, setProjectActionPending] = useState(false)
  const [sessionsLoading, setSessionsLoading] = useState(false)
  const [loadedSessionScopeId, setLoadedSessionScopeId] = useState<string | undefined>(undefined)
  const shouldLoadScopedSessions = isBound && !bindingRequired && Boolean(sessionScopeProjectId)

  useEffect(() => {
    if (activeSessionId || !preferredSessionId) {
      return
    }

    setActiveProjectSession(preferredSessionId)
  }, [activeSessionId, preferredSessionId, setActiveProjectSession])

  useEffect(() => {
    if (!workspace || !sessionScopeProjectId || workspace.currentProjectId !== sessionScopeProjectId) {
      return
    }

    const scopedWorkspaceSessions = workspace.sessions.filter((session) => session.projectId === sessionScopeProjectId)
    if (!scopedWorkspaceSessions.length) {
      return
    }

    setSessions(scopedWorkspaceSessions)
    if (sendPhase !== "idle" || scopedWorkspaceSessions.some((session) => session.id === activeSessionId)) {
      return
    }

    setActiveProjectSession(scopedWorkspaceSessions[0].id)
  }, [
    activeSessionId,
    sendPhase,
    sessionScopeProjectId,
    setActiveProjectSession,
    setSessions,
    workspace
  ])

  useEffect(() => {
    let cancelled = false
    const preserveVisibleConversation = sendPhase !== "idle"

    async function syncScopedSessions() {
      if (!isBound || bindingRequired || !sessionScopeProjectId) {
        if (!cancelled) {
          setSessionsLoading(false)
          setLoadedSessionScopeId(undefined)
          clearSessions()
          if (!preserveVisibleConversation) {
            clearAllSessionMessages()
          }
        }
        return
      }

      setSessionsLoading(true)
      try {
        const nextSessions = await desktopApi.chat.listSessionsByProject(sessionScopeProjectId)
        if (cancelled) {
          return
        }

        setSessions(nextSessions)
        setLoadedSessionScopeId(sessionScopeProjectId)
        if (preserveVisibleConversation) {
          return
        }

        if (nextSessions.some((session) => session.id === activeSessionId)) {
          return
        }

        if (nextSessions[0]) {
          setActiveProjectSession(nextSessions[0].id)
        } else if (sessionScopeProjectId === HOME_CHAT_PROJECT_ID) {
          const homeSession = await desktopApi.chat.createSessionForProject(HOME_CHAT_PROJECT_ID, homeSessionTitle)
          if (cancelled) {
            return
          }
          upsertSession(homeSession)
          setActiveProjectSession(homeSession.id)
        } else {
          setActiveProjectSession(EMPTY_SESSION_ID)
          if (!preserveVisibleConversation) {
            clearAllSessionMessages()
          }
        }
      } catch (error) {
        if (cancelled) {
          return
        }
        clearSessions()
        if (!preserveVisibleConversation) {
          clearAllSessionMessages()
        }
        setErrorText(error instanceof Error ? error.message : String(error))
      } finally {
        if (!cancelled) {
          setSessionsLoading(false)
        }
      }
    }

    void syncScopedSessions()
    return () => {
      cancelled = true
    }
  }, [
    activeSessionId,
    bindingRequired,
    clearAllSessionMessages,
    clearSessions,
    desktopApi.chat,
    homeSessionTitle,
    isBound,
    sendPhase,
    sessionScopeProjectId,
    setActiveProjectSession,
    setErrorText,
    setSessions,
    upsertSession
  ])

  const switchProject = useCallback(async (projectId: string) => {
    if (projectActionPending) {
      return
    }

    const preserveVisibleConversation = sendPhase !== "idle"
    setProjectActionPending(true)
    setErrorText("")
    try {
      const nextWorkspace = await desktopApi.projects.setActive(projectId)
      setWorkspace(nextWorkspace)

      if (nextWorkspace.currentProjectId === projectId) {
        const nextSessions = nextWorkspace.sessions.filter((session) => session.projectId === projectId)
        if (nextSessions.length) {
          setSessions(nextSessions)
          const nextSessionId = resolvePreferredSessionId(nextSessions, activeSessionId)
          if (nextSessionId) {
            setActiveProjectSession(nextSessionId)
          }
        } else if (!preserveVisibleConversation) {
          clearSessions()
          clearAllSessionMessages()
          setActiveProjectSession(EMPTY_SESSION_ID)
        }
      } else if (!preserveVisibleConversation) {
        clearSessions()
        clearAllSessionMessages()
        setActiveProjectSession(EMPTY_SESSION_ID)
      }
    } catch (error) {
      if (!preserveVisibleConversation) {
        clearSessions()
        clearAllSessionMessages()
        setActiveProjectSession(EMPTY_SESSION_ID)
      }
      setErrorText(error instanceof Error ? error.message : String(error))
    } finally {
      setProjectActionPending(false)
    }
  }, [
    activeSessionId,
    clearAllSessionMessages,
    clearSessions,
    desktopApi.projects,
    projectActionPending,
    sendPhase,
    setActiveProjectSession,
    setErrorText,
    setSessions,
    setWorkspace
  ])

  const switchProjectPreservingMessages = useCallback(async (projectId: string) => {
    if (projectActionPending) {
      return
    }

    setProjectActionPending(true)
    setErrorText("")
    try {
      const nextWorkspace = await desktopApi.projects.setActive(projectId)
      setWorkspace(nextWorkspace)
    } catch (error) {
      setErrorText(error instanceof Error ? error.message : String(error))
    } finally {
      setProjectActionPending(false)
    }
  }, [desktopApi.projects, projectActionPending, setErrorText, setWorkspace])

  const createProjectSession = useCallback(async () => {
    if (projectActionPending || !sessionScopeProjectId) {
      return
    }

    setProjectActionPending(true)
    setErrorText("")
    try {
      const session = await desktopApi.chat.createSessionForProject(sessionScopeProjectId)
      upsertSession(session)
      if (viewMode === "experts") {
        const nextWorkspace = await desktopApi.workspace.getSummary().catch(() => null)
        if (nextWorkspace) {
          setWorkspace(nextWorkspace)
        }
      }
      setActiveProjectSession(session.id)
    } catch (error) {
      setErrorText(error instanceof Error ? error.message : String(error))
    } finally {
      setProjectActionPending(false)
    }
  }, [
    desktopApi.chat,
    desktopApi.workspace,
    projectActionPending,
    sessionScopeProjectId,
    setActiveProjectSession,
    setErrorText,
    setWorkspace,
    upsertSession,
    viewMode
  ])

  const closeProjectSession = useCallback(async (sessionId: string) => {
    if (projectActionPending || (sendPhase !== "idle" && activeStreamSessionId === sessionId)) {
      return
    }

    setProjectActionPending(true)
    setErrorText("")
    try {
      const nextSessions = await desktopApi.chat.closeSession(sessionId)
      setSessions(nextSessions)
      if (viewMode === "experts") {
        const nextWorkspace = await desktopApi.workspace.getSummary().catch(() => null)
        if (nextWorkspace) {
          setWorkspace(nextWorkspace)
        }
      }
      const nextSessionId = nextSessions.find((session) => session.id !== sessionId)?.id ?? nextSessions[0]?.id ?? EMPTY_SESSION_ID
      setActiveProjectSession(nextSessionId)
    } catch (error) {
      setErrorText(error instanceof Error ? error.message : String(error))
    } finally {
      setProjectActionPending(false)
    }
  }, [
    activeStreamSessionId,
    desktopApi.chat,
    desktopApi.workspace,
    projectActionPending,
    sendPhase,
    setActiveProjectSession,
    setErrorText,
    setSessions,
    setWorkspace,
    viewMode
  ])

  const openSession = useCallback((sessionId: string) => {
    setActiveProjectSession(sessionId)
  }, [setActiveProjectSession])

  const isCloseProjectSessionDisabled = useCallback((sessionId: string) => {
    return projectActionPending || (sendPhase !== "idle" && activeStreamSessionId === sessionId)
  }, [activeStreamSessionId, projectActionPending, sendPhase])

  return {
    projectActionPending,
    sessionsLoading: sessionsLoading || (shouldLoadScopedSessions && loadedSessionScopeId !== sessionScopeProjectId),
    switchProject,
    switchProjectPreservingMessages,
    createProjectSession,
    closeProjectSession,
    openSession,
    isCloseProjectSessionDisabled
  }
}
