import type { TextareaRenderable } from "@opentui/core"
import { createEffect, createMemo, onMount, onCleanup, on, createSignal } from "solid-js"
import { createStore, unwrap } from "solid-js/store"
import { useLocal } from "@tui/context/local"
import { useSync } from "@tui/context/sync"
import { useArgs } from "@tui/context/args"
import { useDialog } from "@tui/ui/dialog"
import { useRenderer } from "@opentui/solid"
import { useEvent } from "@tui/context/event"
import { TuiEvent } from "../../event"
import { SaeeolSessionTuiSync } from "@/saeeol/session/tui-sync"
import { computePromptTraits } from "./traits"
import type { PromptInfo } from "./history"
import type { AutocompleteRef } from "./autocomplete"
import type { ExtmarkSyncDeps } from "./use-extmark-sync"
import type { PromptProps, PromptRef } from "."

function randomIndex(count: number) {
  if (count <= 0) return 0
  return Math.floor(Math.random() * count)
}

export type StoreShape = {
  prompt: PromptInfo
  mode: "normal" | "shell"
  extmarkToPartIndex: Map<number, number>
  interrupt: number
  exitPress: number
  placeholder: number
}

export type StoreDeps = {
  props: PromptProps
  getInput: () => TextareaRenderable
  extmarkSync: ExtmarkSyncDeps
  restoreExtmarks: (parts: PromptInfo["parts"]) => void
  list: () => string[]
}

let stashed: { prompt: PromptInfo; cursor: number } | undefined

export function usePromptStore(deps: StoreDeps) {
  const local = useLocal()
  const sync = useSync()
  const args = useArgs()
  const dialog = useDialog()
  const renderer = useRenderer()
  const event = useEvent()

  const [store, setStore] = createStore<StoreShape>({
    placeholder: randomIndex(deps.list().length),
    prompt: { input: "", parts: [] },
    mode: "normal",
    extmarkToPartIndex: new Map(),
    interrupt: 0,
    exitPress: 0,
  })

  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")
  })

  createEffect(
    on(
      () => deps.props.sessionID,
      () => { setStore("placeholder", randomIndex(deps.list().length)) },
      { defer: true },
    ),
  )

  let syncedKey: string | undefined
  createEffect(() => {
    const sessionID = deps.props.sessionID
    const msg = lastUserMessage()
    if (!sessionID || !msg) return
    const parts = sync.data.part[msg.id]
    if (!parts) return
    if (!SaeeolSessionTuiSync.model({ role: msg.role, parts })) return
    const key = [sessionID, msg.id].join(":")
    if (key === syncedKey) return
    syncedKey = key
    const primary = local.agent.list().find((x) => x.name === msg.agent)
    if (msg.agent && primary) {
      if (!args.agent) local.agent.set(msg.agent)
      if (msg.model && !primary.model && !args.agent) {
        local.model.set(msg.model)
        local.model.variant.set(msg.model.variant)
      }
    }
  })

  let submitFn: () => Promise<boolean> = async () => false
  const [auto, setAuto] = createSignal<AutocompleteRef>()

  const ref: PromptRef = {
    get focused() { return deps.getInput().focused },
    get current() { return store.prompt },
    focus() { deps.getInput().focus() },
    blur() { deps.getInput().blur() },
    set(prompt) {
      const input = deps.getInput()
      input.setText(prompt.input)
      setStore("prompt", prompt)
      deps.restoreExtmarks(prompt.parts)
      input.gotoBufferEnd()
    },
    reset() {
      const input = deps.getInput()
      input.clear()
      input.extmarks.clear()
      setStore("prompt", { input: "", parts: [] })
      setStore("extmarkToPartIndex", new Map())
    },
    submit() { void submitFn() },
  }

  onMount(() => {
    const saved = stashed
    stashed = undefined
    if (store.prompt.input) return
    if (saved && saved.prompt.input) {
      const input = deps.getInput()
      input.setText(saved.prompt.input)
      setStore("prompt", saved.prompt)
      deps.restoreExtmarks(saved.prompt.parts)
      input.cursorOffset = saved.cursor
    }
  })

  onCleanup(() => {
    if (store.prompt.input) {
      stashed = { prompt: unwrap(store.prompt) as PromptInfo, cursor: deps.getInput().cursorOffset }
    }
    deps.props.ref?.(undefined)
  })

  createEffect(() => {
    if (deps.props.visible === false || deps.props.disabled) auto()?.dismiss()
  })

  createEffect(() => {
    const input = deps.getInput()
    if (!input || input.isDestroyed) return
    if (deps.props.visible === false || dialog.stack.length > 0) {
      if (input.focused) input.blur()
      return
    }
    if (!input.focused) input.focus()
  })

  createEffect(() => {
    const input = deps.getInput()
    if (!input || input.isDestroyed) return
    input.traits = computePromptTraits({
      mode: store.mode,
      disabled: !!deps.props.disabled,
      autocompleteVisible: !!auto()?.visible,
    })
  })

  event.on(TuiEvent.PromptAppend.type, (evt: any) => {
    const input = deps.getInput()
    if (!input || input.isDestroyed) return
    input.insertText(evt.properties.text)
    setTimeout(() => {
      if (!input || input.isDestroyed) return
      input.getLayoutNode().markDirty()
      input.gotoBufferEnd()
      renderer.requestRender()
    }, 0)
  })

  return {
    store,
    setStore,
    ref,
    setAuto,
    setSubmitFn: (fn: () => Promise<boolean>) => { submitFn = fn },
  }
}
