import {
  createEffect,
  createMemo,
  createSignal,
  on,
} from "solid-js"
import { useRoute, useRouteData } from "@tui/context/route"
import { useProject } from "@tui/context/project"
import { useSync } from "@tui/context/sync"
import { useEvent } from "@tui/context/event"
import { useTheme } from "@tui/context/theme"
import { ScrollBoxRenderable } from "@opentui/core"
import type { PromptRef } from "@tui/component/prompt"
import { useLocal } from "@tui/context/local"
import { Locale } from "@/util/locale"
import { useSDK } from "@tui/context/sdk"
import { useEditorContext } from "@tui/context/editor"
import { useKeybind } from "@tui/context/keybind"
import { useDialog } from "../../ui/dialog"
import { errorMessage } from "@/util/error"
import { useToast } from "../../ui/toast"
import { useKV } from "../../context/kv.tsx"
import { usePromptRef } from "../../context/prompt"
import { useExit } from "../../context/exit"
import * as Model from "../../util/model"
import { UI } from "@/cli/ui.ts"
import { session as banner } from "@/saeeol/cli/logo"
import { getScrollAcceleration } from "../../util/scroll"
import { useTerminalDimensions, useKeyboard, useRenderer } from "@opentui/solid"
import { getRevertDiffFiles } from "../../util/revert-diff"
import { useTuiConfig } from "../../context/tui-config"
import { useBellEffects, useUpsellHandler } from "./session-effects"

export function createSessionSetup() {
  const route = useRouteData("session")
  const { navigate } = useRoute()
  const sync = useSync()
  const event = useEvent()
  const project = useProject()
  const tuiConfig = useTuiConfig()
  const kv = useKV()
  const { theme } = useTheme()
  const promptRef = usePromptRef()
  const local = useLocal()
  const session = createMemo(() => sync.session.get(route.sessionID))
  const ch = createMemo(() => {
    const parentID = session()?.parentID ?? session()?.id
    return sync.data.session
      .filter((x) => x.parentID === parentID || x.id === parentID)
      .toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
  })
  const messages = createMemo(() => sync.data.message[route.sessionID] ?? [])
  const permissions = createMemo(() => {
    if (session()?.parentID) return []
    return ch().flatMap((x) => sync.data.permission[x.id] ?? [])
  })
  const questions = createMemo(() => {
    if (session()?.parentID) return []
    return ch().flatMap((x) => sync.data.question[x.id] ?? [])
  })
  const suggestions = createMemo(() => {
    if (session()?.parentID) return []
    return ch().flatMap((x) => sync.data.suggestion[x.id] ?? [])
  })
  const network = createMemo(() => {
    if (session()?.parentID) return []
    return ch().flatMap((x) => sync.data.network[x.id] ?? [])
  })
  const blockingQuestions = createMemo(() => questions().filter((q) => q.blocking !== false))
  const nonBlockingQuestions = createMemo(() => questions().filter((q) => q.blocking === false))
  const question = createMemo(() => blockingQuestions()[0] ?? nonBlockingQuestions()[0])
  const blockingSuggestions = createMemo(() => suggestions().filter((s) => s.blocking !== false))
  const blockingSuggestion = createMemo(() => blockingSuggestions()[0])
  const visible = createMemo(
    () =>
      !session()?.parentID &&
      permissions().length === 0 &&
      blockingQuestions().length === 0 &&
      blockingSuggestions().length === 0 &&
      network().length === 0,
  )
  const networkVisible = createMemo(
    () =>
      permissions().length === 0 &&
      blockingQuestions().length === 0 &&
      blockingSuggestions().length === 0 &&
      network().length > 0,
  )
  const disabled = createMemo(
    () =>
      permissions().length > 0 ||
      blockingQuestions().length > 0 ||
      blockingSuggestions().length > 0 ||
      network().length > 0,
  )
  const pending = createMemo(() => messages().findLast((x) => x.role === "assistant" && !x.time.completed)?.id)
  const lastAssistant = createMemo(() => messages().findLast((x) => x.role === "assistant"))
  const [bellEnabled] = kv.signal("bell_enabled", true)
  useBellEffects({
    sessionID: route.sessionID,
    getStatusType: () => sync.data.session_status?.[route.sessionID]?.type,
    getPermissionsLen: () => permissions().length,
    getQuestionsLen: () => questions().length,
    getSuggestionsNetworkLen: () => suggestions().length + network().length,
    bellEnabled,
  })

  const dimensions = useTerminalDimensions()
  const [sidebar, setSidebar] = kv.signal<"auto" | "hide">("sidebar", "auto")
  const [sidebarOpen, setSidebarOpen] = createSignal(false)
  const [conceal, setConceal] = createSignal(false)
  const [showThinking, setShowThinking] = kv.signal("thinking_visibility", true)
  const [timestamps, setTimestamps] = kv.signal<"hide" | "show">("timestamps", "hide")
  const [showDetails, setShowDetails] = kv.signal("tool_details_visibility", true)
  const [showAssistantMetadata] = kv.signal("assistant_metadata_visibility", true)
  const [showScrollbar, setShowScrollbar] = kv.signal("scrollbar_visible", false)
  const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word")
  const [showGenericToolOutput, setShowGenericToolOutput] = kv.signal("generic_tool_output_visibility", false)
  const [pointing, setPointing] = kv.signal<string | undefined>(`pointing:${route.sessionID}`, undefined)
  const wide = createMemo(() => dimensions().width > 120)
  const sidebarVisible = createMemo(() => {
    if (session()?.parentID) return false
    if (sidebarOpen()) return true
    if (sidebar() === "auto" && wide()) return true
    return false
  })
  const showTimestamps = createMemo(() => timestamps() === "show")
  const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4)
  const providers = createMemo(() => Model.index(sync.data.provider))
  const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig))
  const toast = useToast()
  const sdk = useSDK()
  const editor = useEditorContext()

  createEffect(() => {
    const sessionID = route.sessionID
    void (async () => {
      const previousWorkspace = project.workspace.current()
      const result = await sdk.client.session.get({ sessionID }, { throwOnError: true })
      if (!result.data) {
        toast.show({ message: `Session not found: ${sessionID}`, variant: "error", duration: 5000 })
        navigate({ type: "home" })
        return
      }
      if (result.data.workspaceID !== previousWorkspace) {
        project.workspace.set(result.data.workspaceID)
        try { await sync.bootstrap({ fatal: false }) } catch {}
      }
      editor.reconnect(result.data.directory)
      await sync.session.sync(sessionID)
      if (route.sessionID === sessionID && refs.scroll) refs.scroll.scrollBy(100_000)
    })().catch((error) => {
      if (route.sessionID !== sessionID) return
      toast.show({ message: errorMessage(error), variant: "error", duration: 5000 })
      navigate({ type: "home" })
    })
  })

  let lastSwitch: string | undefined
  event.on("message.part.updated", (evt) => {
    const part = evt.properties.part
    if (part.type !== "tool") return
    if (part.sessionID !== route.sessionID) return
    if (part.state.status !== "completed") return
    if (part.id === lastSwitch) return
    if (part.tool === "plan_enter") { local.agent.set("plan"); lastSwitch = part.id }
  })

  let seeded = false
  const refs = { scroll: null as ScrollBoxRenderable | null, prompt: undefined as PromptRef | undefined }
  const bind = (r: PromptRef | undefined) => {
    refs.prompt = r
    promptRef.set(r)
    if (seeded || !route.prompt || !r) return
    seeded = true
    r.set(route.prompt)
  }
  const keybind = useKeybind()
  const dialog = useDialog()
  const renderer = useRenderer()

  useUpsellHandler({ sessionID: route.sessionID, event, dialog, kv })

  const exit = useExit()
  createEffect(() => {
    const title = Locale.truncate(session()?.title ?? "", 50)
    return exit.message.set(banner(title, session()?.id, UI.Style.TEXT_DIM, UI.Style.TEXT_NORMAL))
  })
  const [exitPress, setExitPress] = createSignal(0)
  useKeyboard((evt) => {
    if (!session()?.parentID) return
    if (keybind.match("app_exit", evt)) {
      if (evt.ctrl && evt.name === "c") {
        evt.preventDefault()
        setExitPress(exitPress() + 1)
        setTimeout(() => setExitPress(0), 1000)
        if (exitPress() >= 2) exit()
        return
      }
      exit()
    }
  })

  function toBottom() {
    setTimeout(() => {
      if (!refs.scroll || refs.scroll.isDestroyed) return
      refs.scroll.scrollTo(refs.scroll.scrollHeight)
    }, 50)
  }

  const revertInfo = createMemo(() => session()?.revert)
  const revertMessageID = createMemo(() => revertInfo()?.messageID)
  const revertDiffFiles = createMemo(() => getRevertDiffFiles(revertInfo()?.diff ?? ""))
  const revertRevertedMessages = createMemo(() => {
    const messageID = revertMessageID()
    if (!messageID) return []
    return messages().filter((x) => x.id >= messageID && x.role === "user")
  })
  const revert = createMemo(() => {
    const info = revertInfo()
    if (!info?.messageID) return
    return { messageID: info.messageID, reverted: revertRevertedMessages(), diff: info.diff, diffFiles: revertDiffFiles() }
  })

  createEffect(on(() => route.sessionID, toBottom))

  return {
    route, navigate, sync, event, project, tuiConfig, kv, theme, promptRef,
    session, ch, messages, permissions, questions, suggestions, network,
    blockingQuestions, nonBlockingQuestions, question, blockingSuggestions, blockingSuggestion,
    visible, networkVisible, disabled, pending, lastAssistant,
    dimensions, sidebar, setSidebar, sidebarOpen, setSidebarOpen, conceal, setConceal,
    showThinking, setShowThinking, timestamps, setTimestamps, showDetails, setShowDetails,
    showAssistantMetadata, showScrollbar, setShowScrollbar, diffWrapMode,
    showGenericToolOutput, setShowGenericToolOutput,
    wide, sidebarVisible, showTimestamps, contentWidth, providers, scrollAcceleration,
    toast, sdk, editor, local, keybind, dialog, renderer,
    refs, bind, toBottom, revert, exit, pointing, setPointing,
  }
}
