import type { ToolPart } from "@saeeol/sdk/v2"
import type { InferParameters, InferMetadata } from "../../tool/tool"
import { GlobTool } from "../../tool/glob"
import { GrepTool } from "../../tool/grep"
import { ReadTool } from "../../tool/read"
import { WriteTool } from "../../tool/write"
import { WebFetchTool } from "../../tool/webfetch"
import { EditTool } from "../../tool/edit"
import { WebSearchTool } from "../../tool/websearch"
import { TaskTool } from "../../tool/task"
import { SkillTool } from "../../tool/skill"
import { BashTool } from "../../tool/bash"
import { TodoWriteTool } from "../../tool/todo"
import { UI } from "../ui"
import { Locale } from "@/util/locale"
import path from "path"

export type ToolProps<T> = {
  input: InferParameters<T>
  metadata: InferMetadata<T>
  part: ToolPart
}

export function props<T>(part: ToolPart): ToolProps<T> {
  const state = part.state
  return {
    input: state.input as InferParameters<T>,
    metadata: ("metadata" in state ? state.metadata : {}) as InferMetadata<T>,
    part,
  }
}

export type Inline = {
  icon: string
  title: string
  description?: string
}

export function inline(info: Inline) {
  const suffix = info.description ? UI.Style.TEXT_DIM + ` ${info.description}` + UI.Style.TEXT_NORMAL : ""
  UI.println(UI.Style.TEXT_NORMAL + info.icon, UI.Style.TEXT_NORMAL + info.title + suffix)
}

function block(info: Inline, output?: string) {
  UI.empty()
  inline(info)
  if (!output?.trim()) return
  UI.println(output)
  UI.empty()
}

export function normalizePath(input?: string) {
  if (!input) return ""
  if (path.isAbsolute(input)) return path.relative(process.cwd(), input) || "."
  return input
}

export function displayTool(part: ToolPart) {
  try {
    if (part.tool === "bash") return bash(props<typeof BashTool>(part))
    if (part.tool === "glob") return glob(props<typeof GlobTool>(part))
    if (part.tool === "grep") return grep(props<typeof GrepTool>(part))
    if (part.tool === "read") return read(props<typeof ReadTool>(part))
    if (part.tool === "write") return write(props<typeof WriteTool>(part))
    if (part.tool === "webfetch") return webfetch(props<typeof WebFetchTool>(part))
    if (part.tool === "edit") return edit(props<typeof EditTool>(part))
    if (part.tool === "websearch") return websearch(props<typeof WebSearchTool>(part))
    if (part.tool === "task") return task(props<typeof TaskTool>(part))
    if (part.tool === "todowrite") return todo(props<typeof TodoWriteTool>(part))
    if (part.tool === "skill") return skill(props<typeof SkillTool>(part))
    return fallback(part)
  } catch {
    return fallback(part)
  }
}

function fallback(part: ToolPart) {
  const state = part.state
  const input = "input" in state ? state.input : undefined
  const title =
    ("title" in state && state.title ? state.title : undefined) ||
    (input && typeof input === "object" && Object.keys(input).length > 0 ? JSON.stringify(input) : "Unknown")
  inline({ icon: "⚙", title: `${part.tool} ${title}` })
}

function glob(info: ToolProps<typeof GlobTool>) {
  const root = info.input.path ?? ""
  const title = `Glob "${info.input.pattern}"`
  const suffix = root ? `in ${normalizePath(root)}` : ""
  const num = info.metadata.count
  const description =
    num === undefined ? suffix : `${suffix}${suffix ? " · " : ""}${num} ${num === 1 ? "match" : "matches"}`
  inline({ icon: "✱", title, ...(description && { description }) })
}

function grep(info: ToolProps<typeof GrepTool>) {
  const root = info.input.path ?? ""
  const title = `Grep "${info.input.pattern}"`
  const suffix = root ? `in ${normalizePath(root)}` : ""
  const num = info.metadata.matches
  const description =
    num === undefined ? suffix : `${suffix}${suffix ? " · " : ""}${num} ${num === 1 ? "match" : "matches"}`
  inline({ icon: "✱", title, ...(description && { description }) })
}

function read(info: ToolProps<typeof ReadTool>) {
  const file = normalizePath(info.input.filePath)
  const pairs = Object.entries(info.input).filter(([key, value]) => {
    if (key === "filePath") return false
    return typeof value === "string" || typeof value === "number" || typeof value === "boolean"
  })
  const description = pairs.length ? `[${pairs.map(([key, value]) => `${key}=${value}`).join(", ")}]` : undefined
  inline({ icon: "→", title: `Read ${file}`, ...(description && { description }) })
}

function write(info: ToolProps<typeof WriteTool>) {
  block(
    { icon: "←", title: `Write ${normalizePath(info.input.filePath)}` },
    info.part.state.status === "completed" ? info.part.state.output : undefined,
  )
}

function webfetch(info: ToolProps<typeof WebFetchTool>) {
  inline({ icon: "%", title: `WebFetch ${info.input.url}` })
}

function edit(info: ToolProps<typeof EditTool>) {
  block(
    { icon: "←", title: `Edit ${normalizePath(info.input.filePath)}` },
    info.metadata.diff,
  )
}

function websearch(info: ToolProps<typeof WebSearchTool>) {
  inline({ icon: "◈", title: `Exa Web Search "${info.input.query}"` })
}

export function displayTask(part: ToolPart) {
  task(props<typeof TaskTool>(part))
}

function task(info: ToolProps<typeof TaskTool>) {
  const input = info.part.state.input
  const status = info.part.state.status
  const subagent =
    typeof input.subagent_type === "string" && input.subagent_type.trim().length > 0 ? input.subagent_type : "unknown"
  const agent = Locale.titlecase(subagent)
  const desc =
    typeof input.description === "string" && input.description.trim().length > 0 ? input.description : undefined
  const icon = status === "error" ? "✗" : status === "running" ? "•" : "✓"
  const name = desc ?? `${agent} Task`
  inline({ icon, title: name, description: desc ? `${agent} Agent` : undefined })
}

function skill(info: ToolProps<typeof SkillTool>) {
  inline({ icon: "→", title: `Skill "${info.input.name}"` })
}

function bash(info: ToolProps<typeof BashTool>) {
  const output = info.part.state.status === "completed" ? info.part.state.output?.trim() : undefined
  block({ icon: "$", title: `${info.input.command}` }, output)
}

function todo(info: ToolProps<typeof TodoWriteTool>) {
  block(
    { icon: "#", title: "Todos" },
    info.input.todos.map((item: { status: string; content: string }) => `${item.status === "completed" ? "[x]" : "[ ]"} ${item.content}`).join("\n"),
  )
}
