import { createMemo, createSignal } from "solid-js"
import { RGBA } from "@opentui/core"
import path from "path"
import { useLocal } from "@tui/context/local"
import { tint, useTheme } from "@tui/context/theme"
import { useSync } from "@tui/context/sync"
import { useEditorContext, editorSelectionKey, type EditorSelection } from "@tui/context/editor"
import { useTerminalDimensions } from "@opentui/solid"
import { useKeybind } from "@tui/context/keybind"
import { useKV } from "../../context/kv"
import { clamp } from "@/boxes/clamp"
import { Locale } from "@/util/locale"
import { createFadeIn } from "../../util/signal"
import { createColors, createFrames } from "../../ui/spinner"
import type { AssistantMessage } from "@saeeol/sdk/v2"
import type { PromptProps } from "."
import type { PromptInfo } from "./history"

export const money = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
})

export function hasEditorRangeSelection(selection: EditorSelection["ranges"][number]) {
  return (
    selection.selection.start.line !== selection.selection.end.line ||
    selection.selection.start.character !== selection.selection.end.character
  )
}

export function getEditorRangeLabel(selection: EditorSelection["ranges"][number]) {
  if (!hasEditorRangeSelection(selection)) return
  if (selection.selection.start.line === selection.selection.end.line) return `#${selection.selection.start.line}`
  return `#${selection.selection.start.line}-${selection.selection.end.line}`
}

export function formatEditorContext(selection: EditorSelection) {
  const selected = selection.ranges.filter(hasEditorRangeSelection)
  if (selected.length === 0)
    return `<system-reminder>Note: The user opened the file "${selection.filePath}". This may or may not be relevant to the current task.</system-reminder>\n`
  const ranges = selected.map((range, index) => {
    const prefix = selected.length > 1 ? `Selection ${index + 1}: ` : ""
    return `Note: The user selected ${prefix}${getEditorRangeLabel(range)} from "${selection.filePath}". \`\`\`${range.text}\`\`\`\n\n`
  })
  return `<system-reminder>${ranges.join("\n")} This may or may not be relevant to the current task.</system-reminder>\n`
}

export type PromptMemos = {
  status: () => { type: string }
  usage: () => { context: string; cost?: string; health: string; remaining?: number; pct: number } | undefined
  currentProviderLabel: () => string
  hasRightContent: () => boolean
  editorContext: () => EditorSelection | undefined
  editorPath: () => string | undefined
  editorSelectionLabel: () => string | undefined
  editorFileLabel: () => string | undefined
  editorFileLabelDisplay: () => string | undefined
  highlight: () => RGBA
  showVariant: () => boolean
  agentMetaAlpha: () => number
  modelMetaAlpha: () => number
  variantMetaAlpha: () => number
  borderHighlight: () => RGBA
  placeholderText: () => string | undefined
  spinnerDef: () => { frames: any; color: any }
  list: () => string[]
  shell: () => string[]
  dismissedEditorSelectionKey: () => string | undefined
  setDismissedEditorSelectionKey: (v: string | undefined) => void
}

export type MemoDeps = {
  props: PromptProps
  store: {
    prompt: PromptInfo
    mode: "normal" | "shell"
    placeholder: number
  }
}

export function usePromptMemos(deps: MemoDeps): PromptMemos {
  const local = useLocal()
  const sync = useSync()
  const editor = useEditorContext()
  const { theme, syntax } = useTheme()
  const keybind = useKeybind()
  const dimensions = useTerminalDimensions()
  const kv = useKV()

  const animationsEnabled = createMemo(() => kv.get("animations_enabled", true))
  const list = createMemo(() => deps.props.placeholders?.normal ?? [])
  const shell = createMemo(() => deps.props.placeholders?.shell ?? [])
  const fileContextEnabled = createMemo(() => kv.get("file_context_enabled", true))
  const [dismissedEditorSelectionKey, setDismissedEditorSelectionKey] = createSignal<string>()

  const status = createMemo(() => sync.data.session_status?.[deps.props.sessionID ?? ""] ?? { type: "idle" })

  const editorContext = createMemo(() => {
    const selection = fileContextEnabled() ? editor.selection() : undefined
    if (!selection) return
    return editorSelectionKey(selection) === dismissedEditorSelectionKey() ? undefined : selection
  })

  const editorPath = createMemo(() => editorContext()?.filePath)
  const editorSelectionLabel = createMemo(() => {
    const ranges = editorContext()?.ranges
    if (!ranges) return
    const first = ranges.find(hasEditorRangeSelection) ?? ranges[0]
    if (!first) return
    return [getEditorRangeLabel(first), ranges.length > 1 ? `+${ranges.length - 1}` : undefined]
      .filter(Boolean)
      .join(" ")
  })
  const editorFileLabel = createMemo(() => {
    const value = editorPath()
    if (!value) return
    const filename = path.basename(value)
    const file = /^index\.[^./]+$/.test(filename)
      ? [path.basename(path.dirname(value)), filename].filter(Boolean).join("/")
      : filename
    return `${file.split(path.sep).join("/")}${editorSelectionLabel() ?? ""}`
  })
  const editorFileLabelDisplay = createMemo(() => {
    const file = editorFileLabel()
    if (!file) return
    return Locale.truncateMiddle(file, clamp(Math.floor(dimensions().width / 3), 12, 48))
  })
  const currentProviderLabel = createMemo(() => local.model.parsed().provider)
  const hasRightContent = createMemo(() => Boolean(deps.props.right))

  const lastUserMessage = createMemo(() => {
    if (!deps.props.sessionID) return undefined
    const messages = sync.data.message[deps.props.sessionID]
    if (!messages) return undefined
    return messages.findLast((m): m is import("@saeeol/sdk/v2").UserMessage => m.role === "user")
  })

  const usage = createMemo(() => {
    if (!deps.props.sessionID) return
    const msg = sync.data.message[deps.props.sessionID] ?? []
    const last = msg.findLast((item): item is AssistantMessage => item.role === "assistant" && item.tokens.output > 0)
    if (!last) return
    const tokens = last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
    if (tokens <= 0) return
    const model = sync.data.provider.find((item) => item.id === last.providerID)?.models?.[last.modelID]
    const contextLimit = model?.limit.context ?? 0
    const pct = contextLimit ? Math.round((tokens / contextLimit) * 100) : 0
    const cost = msg.reduce((sum, item) => sum + (item.role === "assistant" ? item.cost : 0), 0)
    const health = !contextLimit ? "ok" : pct >= 90 ? "critical" : pct >= 75 ? "warning" : "ok"
    const remaining = contextLimit ? Math.max(0, contextLimit - tokens) : undefined
    return {
      context: contextLimit ? `${Locale.number(tokens)} (${pct}%)` : Locale.number(tokens),
      cost: cost > 0 ? money.format(cost) : undefined,
      health,
      remaining,
      pct,
    }
  })

  const highlight = createMemo(() => {
    if (keybind.leader) return theme.border
    if (deps.store.mode === "shell") return theme.primary
    const agent = local.agent.current()
    if (!agent) return theme.border
    return local.agent.color(agent.name ?? "")
  })

  const showVariant = createMemo(() => {
    const variants = local.model.variant.list()
    if (variants.length === 0) return false
    const current = local.model.variant.current()
    return !!current
  })

  const agentMetaAlpha = createFadeIn(() => !!local.agent.current(), animationsEnabled)
  const modelMetaAlpha = createFadeIn(() => !!local.agent.current() && deps.store.mode === "normal", animationsEnabled)
  const variantMetaAlpha = createFadeIn(
    () => !!local.agent.current() && deps.store.mode === "normal" && showVariant(),
    animationsEnabled,
  )
  const borderHighlight = createMemo(() => tint(theme.border, highlight(), agentMetaAlpha()))

  const placeholderText = createMemo(() => {
    if (deps.props.showPlaceholder === false) return undefined
    if (deps.store.mode === "shell") {
      if (!shell().length) return undefined
      const example = shell()[deps.store.placeholder % shell().length]
      return `Run a command... "${example}"`
    }
    if (!list().length) return undefined
    return `Ask anything... "${list()[deps.store.placeholder % list().length]}"`
  })

  const spinnerDef = createMemo(() => {
    const agent = local.agent.current()
    const color = agent ? local.agent.color(agent.name ?? "") : theme.border
    return {
      frames: createFrames({ color, style: "blocks", inactiveFactor: 0.6, minAlpha: 0.3 }),
      color: createColors({ color, style: "blocks", inactiveFactor: 0.6, minAlpha: 0.3 }),
    }
  })

  return {
    status, usage, currentProviderLabel, hasRightContent,
    editorContext, editorPath, editorSelectionLabel, editorFileLabel, editorFileLabelDisplay,
    highlight, showVariant, agentMetaAlpha, modelMetaAlpha, variantMetaAlpha,
    borderHighlight, placeholderText, spinnerDef, list, shell,
    dismissedEditorSelectionKey, setDismissedEditorSelectionKey,
  }
}
