/** Textarea JSX + 이벤트 핸들러 — index.tsx에서 분리 */

import type { TextareaRenderable, PasteEvent, MouseEvent } from "@opentui/core"
import { decodePasteBytes } from "@opentui/core"
import { fileURLToPath } from "url"
import path from "path"
import { Filesystem } from "@/util/filesystem"
import { iife } from "@/util/iife"
import * as Clipboard from "../../util/clipboard"
import { useKeybind } from "@tui/context/keybind"
import { useTheme } from "@tui/context/theme"
import { useExit } from "../../context/exit"
import { useRenderer } from "@opentui/solid"
import { useKV } from "../../context/kv"
import { useSync } from "@tui/context/sync"
import { useTextareaKeybindings } from "../textarea-keybindings"
import type { PromptInfo } from "./history"
import type { PromptRef } from "."
import type { StoreShape } from "./use-prompt-store"

export type TextareaDeps = {
  props: {
    visible?: boolean
    disabled?: boolean
    showPlaceholder?: boolean
    placeholders?: { normal?: string[]; shell?: string[] }
    ref?: (ref: PromptRef | undefined) => void
  }
  getStore: () => StoreShape
  setStore: (key: string, ...args: any[]) => void
  setStoreFull: (key: any, value: any) => void
  getInput: () => TextareaRenderable
  autocomplete: { onInput: (v: string) => void; onKeyDown: (e: any) => void; onCursorChange: () => void; visible: boolean }
  submit: () => Promise<boolean>
  pasteText: (text: string, virtualText: string) => void
  pasteAttachment: (file: { filename?: string; filepath?: string; content: string; mime: string }) => Promise<void>
  restoreExtmarks: (parts: PromptInfo["parts"]) => void
  syncExtmarks: () => void
  history: { move: (dir: number, text: string) => PromptInfo | undefined }
  placeholderText: () => string | undefined
  promptPartTypeId: number
  ref: PromptRef
}

export function PromptTextarea(deps: TextareaDeps) {
  const keybind = useKeybind()
  const { theme, syntax } = useTheme()
  const exit = useExit()
  const renderer = useRenderer()
  const kv = useKV()
  const sync = useSync()
  const textareaKeybindings = useTextareaKeybindings()

  return (
    <textarea
      placeholder={deps.placeholderText()}
      placeholderColor={theme.textMuted}
      textColor={keybind.leader ? theme.textMuted : theme.text}
      focusedTextColor={keybind.leader ? theme.textMuted : theme.text}
      minHeight={1}
      maxHeight={6}
      onContentChange={() => {
        const input = deps.getInput()
        const value = input.plainText
        deps.setStore("prompt", "input", value)
        deps.autocomplete.onInput(value)
        deps.syncExtmarks()
      }}
      onCursorChange={() => {
        const store = deps.getStore()
        if (store.mode === "normal") deps.autocomplete.onCursorChange()
      }}
      keyBindings={textareaKeybindings()}
      onKeyDown={async (e) => {
        const input = deps.getInput()
        const store = deps.getStore()
        if (deps.props.disabled) { e.preventDefault(); return }
        if (keybind.match("input_paste", e)) {
          const content = await Clipboard.read()
          if (content?.mime.startsWith("image/")) {
            e.preventDefault()
            await deps.pasteAttachment({ filename: "clipboard", mime: content.mime, content: content.data })
            return
          }
        }
        if (keybind.match("input_clear", e) && store.prompt.input !== "") {
          input.clear(); input.extmarks.clear()
          deps.setStoreFull("prompt", { input: "", parts: [] })
          deps.setStoreFull("extmarkToPartIndex", new Map())
          return
        }
        if (keybind.match("app_exit", e)) {
          if (store.prompt.input === "") {
            if (e.ctrl && e.name === "c") {
              deps.setStore("exitPress", store.exitPress + 1)
              setTimeout(() => deps.setStore("exitPress", 0), 1000)
              if (store.exitPress >= 2) { await exit(); e.preventDefault(); return }
              e.preventDefault(); return
            }
            await exit(); e.preventDefault(); return
          }
        }
        if (e.name === "!" && input.visualCursor.offset === 0) {
          deps.setStoreFull("placeholder", Math.floor(Math.random() * ((deps.props.placeholders?.shell?.length ?? 0) || 1)))
          deps.setStore("mode", "shell"); e.preventDefault(); return
        }
        if (store.mode === "shell") {
          if ((e.name === "backspace" && input.visualCursor.offset === 0) || e.name === "escape") {
            deps.setStore("mode", "normal"); e.preventDefault(); return
          }
        }
        if (store.mode === "normal") deps.autocomplete.onKeyDown(e)
        if (!deps.autocomplete.visible) {
          if ((keybind.match("history_previous", e) && input.cursorOffset === 0) || (keybind.match("history_next", e) && input.cursorOffset === input.plainText.length)) {
            const direction = keybind.match("history_previous", e) ? -1 : 1
            const item = deps.history.move(direction, input.plainText)
            if (item) {
              input.setText(item.input)
              deps.setStoreFull("prompt", item)
              deps.setStoreFull("mode", item.mode ?? "normal")
              deps.restoreExtmarks(item.parts)
              e.preventDefault()
              if (direction === -1) input.cursorOffset = 0
              if (direction === 1) input.cursorOffset = input.plainText.length
            }
            return
          }
          if (keybind.match("history_previous", e) && input.visualCursor.visualRow === 0) input.cursorOffset = 0
          if (keybind.match("history_next", e) && input.visualCursor.visualRow === input.height - 1) input.cursorOffset = input.plainText.length
        }
      }}
      onSubmit={() => setTimeout(() => setTimeout(() => deps.submit(), 0), 0)}
      onPaste={async (event: PasteEvent) => {
        const input = deps.getInput()
        if (deps.props.disabled) { event.preventDefault(); return }
        const normalizedText = decodePasteBytes(event.bytes).replace(/\r\n/g, "\n").replace(/\r/g, "\n")
        const pastedContent = normalizedText.trim()
        if (!pastedContent) return
        event.preventDefault()
        const filepath = iife(() => {
          const raw = pastedContent.replace(/^['"]+|['"]+$/g, "")
          if (raw.startsWith("file://")) { try { return fileURLToPath(raw) } catch {} }
          if (process.platform === "win32") return raw
          return raw.replace(/\\(.)/g, "$1")
        })
        const isUrl = /^(https?):\/\//.test(filepath)
        if (!isUrl) {
          try {
            const mime = await Filesystem.mimeType(filepath)
            const filename = path.basename(filepath)
            if (mime === "image/svg+xml") {
              const content = await Filesystem.readText(filepath).catch(() => {})
              if (content) { deps.pasteText(content, `[SVG: ${filename ?? "image"}]`); return }
            }
            if (mime.startsWith("image/") || mime === "application/pdf") {
              const content = await Filesystem.readArrayBuffer(filepath).then((b) => Buffer.from(b).toString("base64")).catch(() => {})
              if (content) { await deps.pasteAttachment({ filename, filepath, mime, content }); return }
            }
          } catch {}
        }
        const lineCount = (pastedContent.match(/\n/g)?.length ?? 0) + 1
        if ((lineCount >= 5 || pastedContent.length > 800) && kv.get("paste_summary_enabled", !sync.data.config.experimental?.disable_paste_summary)) {
          deps.pasteText(pastedContent, `[Pasted ~${lineCount} lines]`); return
        }
        input.insertText(normalizedText)
        setTimeout(() => { if (!input || input.isDestroyed) return; input.getLayoutNode().markDirty(); renderer.requestRender() }, 0)
      }}
      ref={(r: TextareaRenderable) => {
        const ppti = deps.promptPartTypeId
        if (ppti === 0) (deps as any).promptPartTypeId = r.extmarks.registerType("prompt-part")
        deps.props.ref?.(deps.ref)
        setTimeout(() => { if (!r || r.isDestroyed) return; r.cursorColor = theme.text }, 0)
      }}
      onMouseDown={(r: MouseEvent) => r.target?.focus()}
      focusedBackgroundColor={theme.backgroundElement}
      cursorColor={theme.text}
      syntaxStyle={syntax()}
    />
  )
}
