import type { TextareaRenderable } from "@opentui/core"
import { produce, type SetStoreFunction } from "solid-js/store"
import { useLocal } from "@tui/context/local"
import { useSDK } from "@tui/context/sdk"
import { useSync } from "@tui/context/sync"
import { useRoute } from "@tui/context/route"
import { useProject } from "@tui/context/project"
import { useEditorContext, editorSelectionKey } from "@tui/context/editor"
import { useDialog } from "@tui/ui/dialog"
import { useToast } from "../../ui/toast"
import { MessageID, PartID } from "@/session/schema"
import { iife } from "@/util/iife"
import { DialogProvider as DialogProviderConnect } from "../dialog-provider"
import { DialogWorkspaceCreate, restoreWorkspaceSession } from "../dialog-workspace-create"
import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
import { assign } from "./part"
import type { PromptInfo } from "./history"
import type { AutocompleteRef } from "./autocomplete"
import type { ExtmarkSyncDeps } from "./use-extmark-sync"
import { formatEditorContext } from "./use-prompt-memos"
import type { PromptProps } from "."
import type { StoreShape } from "./use-prompt-store"

export type SubmitDeps = {
  props: PromptProps
  getInput: () => TextareaRenderable
  store: StoreShape
  setStore: SetStoreFunction<StoreShape>
  autocomplete: () => AutocompleteRef | undefined
  extmarkSync: ExtmarkSyncDeps
  syncExtmarks: () => void
  history: { append: (info: PromptInfo & { mode: "normal" | "shell" }) => void }
  lastSubmittedEditorSelectionKey: { value: string | undefined }
  editorContext: () => any
  exit: () => Promise<void>
}

export function usePromptSubmit(deps: SubmitDeps) {
  const local = useLocal()
  const sdk = useSDK()
  const sync = useSync()
  const route = useRoute()
  const project = useProject()
  const dialog = useDialog()
  const toast = useToast()

  function promptModelWarning() {
    toast.show({ variant: "warning", message: "Connect a provider to send prompts", duration: 3000 })
    if (sync.data.provider.length === 0) dialog.replace(() => <DialogProviderConnect />)
  }

  async function submit(): Promise<boolean> {
    const input = deps.getInput()
    if (input && !input.isDestroyed && input.plainText !== deps.store.prompt.input) {
      deps.setStore("prompt", "input", input.plainText)
      deps.syncExtmarks()
    }
    if (deps.props.disabled) return false
    if (deps.autocomplete()?.visible) return false
    if (!deps.store.prompt.input) return false
    const agent = local.agent.current()
    if (!agent) return false
    const trimmed = deps.store.prompt.input.trim()
    if (trimmed === "exit" || trimmed === "quit" || trimmed === ":q") {
      void deps.exit()
      return true
    }
    const selectedModel = local.model.current()
    if (!selectedModel) {
      void promptModelWarning()
      return false
    }

    const ws = deps.props.sessionID ? sync.session.get(deps.props.sessionID) : undefined
    const wsID = ws?.workspaceID
    const wsStatus = wsID ? (project.workspace.status(wsID) ?? "error") : undefined
    if (deps.props.sessionID && wsID && wsStatus !== "connected") {
      dialog.replace(() => (
        <DialogWorkspaceUnavailable
          onRestore={() => {
            dialog.replace(() => (
              <DialogWorkspaceCreate
                onSelect={(id) =>
                  restoreWorkspaceSession({ dialog, sdk, sync, project, toast, workspaceID: id, sessionID: deps.props.sessionID! })
                }
              />
            ))
          }}
        />
      ))
      return false
    }

    let sessionID = deps.props.sessionID
    if (sessionID == null) {
      const res = await sdk.client.session.create({ workspace: deps.props.workspaceID })
      if (res.error) {
        console.log("Creating a session failed:", res.error)
        toast.show({ message: "Creating a session failed. Open console for more details.", variant: "error" })
        return true
      }
      sessionID = res.data.id
    }

    const messageID = MessageID.ascending()
    let inputText = deps.store.prompt.input
    const allExtmarks = input.extmarks.getAllForTypeId(deps.extmarkSync.promptPartTypeId)
    const sorted = allExtmarks.sort((a: { start: number }, b: { start: number }) => b.start - a.start)
    for (const extmark of sorted) {
      const idx = deps.store.extmarkToPartIndex.get(extmark.id)
      if (idx !== undefined) {
        const part = deps.store.prompt.parts[idx]
        if (part?.type === "text" && part.text) {
          inputText = inputText.slice(0, extmark.start) + part.text + inputText.slice(extmark.end)
        }
      }
    }

    const nonTextParts = deps.store.prompt.parts.filter((p) => p.type !== "text")
    const currentMode = deps.store.mode
    const variant = local.model.variant.current()
    const editorSel = deps.editorContext()
    const editorKey = editorSelectionKey(editorSel)
    const editorParts =
      editorSel && editorKey !== deps.lastSubmittedEditorSelectionKey.value
        ? [{ id: PartID.ascending(), type: "text" as const, text: formatEditorContext(editorSel), synthetic: true, metadata: { kind: "editor_context", source: editorSel.source ?? "editor", filePath: editorSel.filePath, ranges: editorSel.ranges } }]
        : []

    if (currentMode === "shell") {
      void sdk.client.session.shell({ sessionID, agent: agent.name ?? "", model: { providerID: selectedModel.providerID, modelID: selectedModel.modelID }, command: inputText })
      deps.setStore("mode", "normal")
    } else if (
      inputText.startsWith("/") &&
      iife(() => { const cmd = inputText.split("\n")[0].split(" ")[0].slice(1); return sync.data.command.some((x) => x.name === cmd) })
    ) {
      const firstLineEnd = inputText.indexOf("\n")
      const firstLine = firstLineEnd === -1 ? inputText : inputText.slice(0, firstLineEnd)
      const [cmd, ...lineArgs] = firstLine.split(" ")
      const rest = firstLineEnd === -1 ? "" : inputText.slice(firstLineEnd + 1)
      void sdk.client.session.command({ sessionID, command: cmd.slice(1), arguments: lineArgs.join(" ") + (rest ? "\n" + rest : ""), agent: agent.name ?? "", model: `${selectedModel.providerID}/${selectedModel.modelID}`, messageID, variant, parts: nonTextParts.filter((x) => x.type === "file").map((x) => ({ id: PartID.ascending(), ...x })) })
    } else {
      sdk.client.session.prompt({ sessionID, ...selectedModel, messageID, agent: agent.name ?? "", model: selectedModel, variant, parts: [...editorParts, { id: PartID.ascending(), type: "text", text: inputText }, ...nonTextParts.map(assign)] }).catch(() => {})
      deps.lastSubmittedEditorSelectionKey.value = editorKey
    }
    toast.dismiss()
    deps.history.append({ ...deps.store.prompt, mode: currentMode })
    input.extmarks.clear()
    deps.setStore("prompt", { input: "", parts: [] })
    deps.setStore("extmarkToPartIndex", new Map())
    deps.props.onSubmit?.()
    if (!deps.props.sessionID) setTimeout(() => { route.navigate({ type: "session", sessionID }) }, 50)
    input.clear()
    return true
  }

  function pasteText(text: string, virtualText: string) {
    const input = deps.getInput()
    const offset = input.visualCursor.offset
    const start = offset
    const end = start + virtualText.length
    input.insertText(virtualText + " ")
    const id = input.extmarks.create({ start, end, virtual: true, styleId: deps.extmarkSync.pasteStyleId, typeId: deps.extmarkSync.promptPartTypeId })
    deps.setStore(produce((draft: any) => { const idx = draft.prompt.parts.length; draft.prompt.parts.push({ type: "text" as const, text, source: { text: { start, end, value: virtualText } } }); draft.extmarkToPartIndex.set(id, idx) }))
  }

  async function pasteAttachment(file: { filename?: string; filepath?: string; content: string; mime: string }) {
    const input = deps.getInput()
    const start = input.visualCursor.offset
    const pdf = file.mime === "application/pdf"
    const count = deps.store.prompt.parts.filter((x: any) => x.type === "file" && (pdf ? x.mime === "application/pdf" : x.mime.startsWith("image/"))).length
    const virtualText = pdf ? `[PDF ${count + 1}]` : `[Image ${count + 1}]`
    const end = start + virtualText.length
    input.insertText(virtualText + " ")
    const id = input.extmarks.create({ start, end, virtual: true, styleId: deps.extmarkSync.pasteStyleId, typeId: deps.extmarkSync.promptPartTypeId })
    deps.setStore(produce((draft: any) => { const idx = draft.prompt.parts.length; draft.prompt.parts.push({ type: "file" as const, mime: file.mime, filename: file.filename, url: `data:${file.mime};base64,${file.content}`, source: { type: "file", path: file.filepath ?? file.filename ?? "", text: { start, end, value: virtualText } } }); draft.extmarkToPartIndex.set(id, idx) }))
  }

  return { submit, pasteText, pasteAttachment, promptModelWarning }
}
