import { createEffect, onCleanup, onMount, batch } from "solid-js"
import { useKeyboard } from "@opentui/solid"
import * as Clipboard from "@tui/util/clipboard"
import * as Selection from "@tui/util/selection"
import { Flag } from "@saeeol/core/flag/flag"
import { Session as SessionApi } from "@/session/session"
import { Provider } from "@/provider/provider"
import * as SaeeolApp from "@/saeeol/cli/cmd/tui/app"
import { useArgs } from "@tui/context/args"

type Renderer = ReturnType<typeof import("@opentui/solid").useRenderer>
type Route = ReturnType<typeof import("@tui/context/route").useRoute>
type Sync = ReturnType<typeof import("@tui/context/sync").useSync>
type Local = ReturnType<typeof import("@tui/context/local").useLocal>
type SDK = ReturnType<typeof import("@tui/context/sdk").useSDK>
type Toast = ReturnType<typeof import("@tui/ui/toast").useToast>

export function setupSelectionHandlers(deps: {
  renderer: Renderer
  toast: Toast
}) {
  useKeyboard((evt) => {
    if (!Flag.SAEEOL_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) return
    const sel = deps.renderer.getSelection()
    if (!sel) return
    if (evt.ctrl && evt.name === "c") {
      if (!Selection.copy(deps.renderer, deps.toast)) {
        deps.renderer.clearSelection()
        return
      }
      evt.preventDefault()
      evt.stopPropagation()
      return
    }
    if (evt.name === "escape") {
      deps.renderer.clearSelection()
      evt.preventDefault()
      evt.stopPropagation()
      return
    }
    const focus = deps.renderer.currentFocusedRenderable
    if (focus?.hasSelection() && sel.selectedRenderables.includes(focus)) {
      return
    }
    deps.renderer.clearSelection()
  })
  deps.renderer.console.onCopySelection = async (text: string) => {
    if (!text || text.length === 0) return
    await Clipboard.copy(text)
      .then(() => deps.toast.show({ message: "Copied to clipboard", variant: "info" }))
      .catch(deps.toast.error)
    deps.renderer.clearSelection()
  }
}

export function setupTerminalTitle(deps: {
  route: Route
  sync: Sync
  renderer: Renderer
  enabled: () => boolean
}) {
  let spinnerTimer: ReturnType<typeof setInterval> | undefined
  let spinnerFrame = 0
  onCleanup(() => {
    if (spinnerTimer) clearInterval(spinnerTimer)
  })
  createEffect(() => {
    if (!deps.enabled() || Flag.SAEEOL_DISABLE_TERMINAL_TITLE) return
    const titleDefault = SaeeolApp.APP_TITLE
    if (spinnerTimer) {
      clearInterval(spinnerTimer)
      spinnerTimer = undefined
    }
    if (deps.route.data.type === "home") {
      deps.renderer.setTerminalTitle(`${SaeeolApp.STATUS_ICON.idle} ${titleDefault}`)
      return
    }
    if (deps.route.data.type === "session") {
      const session = deps.sync.session.get(deps.route.data.sessionID)
      const status = deps.sync.data.session_status[deps.route.data.sessionID]
      const statusType = status?.type ?? "idle"
      const isBusy = statusType === "busy"
      const buildTitle = (icon: string) => {
        if (!session || SessionApi.isDefaultTitle(session.title)) {
          return `${icon} ${titleDefault}`
        }
        const title = session.title.length > 40 ? session.title.slice(0, 37) + "..." : session.title
        return `${icon} ${titleDefault} | ${title}`
      }
      if (isBusy) {
        const update = () => {
          const icon = SaeeolApp.SPINNER_FRAMES[spinnerFrame % SaeeolApp.SPINNER_FRAMES.length]
          spinnerFrame++
          deps.renderer.setTerminalTitle(buildTitle(icon))
        }
        update()
        spinnerTimer = setInterval(update, SaeeolApp.SPINNER_INTERVAL)
        return
      }
      const icon = SaeeolApp.STATUS_ICON[statusType] ?? SaeeolApp.STATUS_ICON.idle
      deps.renderer.setTerminalTitle(buildTitle(icon))
      return
    }
    if (deps.route.data.type === "plugin") {
      deps.renderer.setTerminalTitle(`${SaeeolApp.STATUS_ICON.idle} ${titleDefault} | ${deps.route.data.id}`)
    }
    const saeeolTitle = SaeeolApp.getTerminalTitle(deps.route, titleDefault)
    if (saeeolTitle) deps.renderer.setTerminalTitle(`${SaeeolApp.STATUS_ICON.idle} ${saeeolTitle}`)
  })
}

export function setupSessionNavigation(deps: {
  local: Local
  sync: Sync
  sdk: SDK
  route: Route
  toast: Toast
}) {
  const args = useArgs()
  onMount(() => {
    batch(() => {
      if (args.agent) deps.local.agent.set(args.agent)
      if (args.model) {
        const { providerID, modelID } = Provider.parseModel(args.model)
        if (!providerID || !modelID)
          return deps.toast.show({
            variant: "warning",
            message: `Invalid model format: ${args.model}`,
            duration: 3000,
          })
        deps.local.model.set({ providerID, modelID }, { recent: true })
      }
      if (args.sessionID && !args.fork) {
        deps.route.navigate({
          type: "session",
          sessionID: args.sessionID,
        })
      }
    })
  })
  let continued = false
  createEffect(() => {
    if (continued || deps.sync.status === "loading" || !args.continue) return
    const match = deps.sync.data.session
      .toSorted((a, b) => b.time.updated - a.time.updated)
      .find((x) => x.parentID === undefined)?.id
    if (match) {
      continued = true
      if (args.fork) {
        void deps.sdk.client.session.fork({ sessionID: match }).then((result) => {
          if (result.data?.id) {
            deps.route.navigate({ type: "session", sessionID: result.data.id })
          } else {
            deps.toast.show({ message: "Failed to fork session", variant: "error" })
          }
        })
      } else {
        deps.route.navigate({ type: "session", sessionID: match })
      }
    }
  })
  let forked = false
  createEffect(() => {
    if (forked || deps.sync.status !== "complete" || !args.sessionID || !args.fork) return
    forked = true
    void deps.sdk.client.session.fork({ sessionID: args.sessionID }).then((result) => {
      if (result.data?.id) {
        deps.route.navigate({ type: "session", sessionID: result.data.id })
      } else {
        deps.toast.show({ message: "Failed to fork session", variant: "error" })
      }
    })
  })
}
