// Renders an assistant message: GFM markdown for prose, custom ```card```
// blocks rendered as rich UI (status pills, tenant cards, KPIs), pills for
// command-bus action blocks, and tool-result cards (role: "tool").

import { type ReactNode, useMemo } from "react"
import ReactMarkdown from "react-markdown"
import remarkGfm from "remark-gfm"
import { Sparkles, Wrench } from "lucide-react"

import { extractActionBlocks } from "@crema/action-bus"
import { stripToolCallTags, type ToolCall } from "@crema/llm-ui"

const ACTION_BLOCK_RE = /```action\s*\n[\s\S]*?```/g
const CARD_BLOCK_RE = /```card\s*\n([\s\S]*?)```/g

export type MessageBodyProps = {
  content: string
  isToolResult?: boolean
  toolCalls?: ToolCall[]
}

type CardSpec =
  | { kind: "pill"; status: string; label?: string }
  | { kind: "stat"; label: string; value: string | number; tone?: string }
  | { kind: "callout"; title?: string; tone?: "info" | "warning" | "danger" | "success"; body?: string }
  | { kind: string; [k: string]: unknown }

function parseCardBlocks(content: string): { blocks: CardSpec[]; stripped: string } {
  const blocks: CardSpec[] = []
  CARD_BLOCK_RE.lastIndex = 0
  const stripped = content.replace(CARD_BLOCK_RE, (_, body: string) => {
    try {
      const parsed = JSON.parse(body.trim()) as CardSpec
      if (parsed && typeof parsed === "object" && typeof parsed.kind === "string") {
        blocks.push(parsed)
        return "" // strip from prose
      }
    } catch {
      // malformed — leave the original block in the prose so the user can see
      // what the model tried to emit.
      return _
    }
    return _
  })
  return { blocks, stripped }
}

function renderCard(spec: CardSpec): ReactNode {
  switch (spec.kind) {
    case "pill": {
      const s = spec as { kind: "pill"; status: string; label?: string }
      const tone =
        s.status === "active"
          ? "border-emerald-500/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300"
          : s.status === "suspended"
            ? "border-amber-500/40 bg-amber-500/15 text-amber-700 dark:text-amber-300"
            : s.status === "deactivated"
              ? "border-rose-500/40 bg-rose-500/15 text-rose-700 dark:text-rose-300"
              : "border-border bg-muted text-muted-foreground"
      return (
        <span
          className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium ${tone}`}
        >
          {s.label ?? s.status}
        </span>
      )
    }
    case "stat": {
      const s = spec as { kind: "stat"; label: string; value: string | number }
      return (
        <span className="inline-flex items-baseline gap-1.5 rounded-md border bg-card px-2 py-1 text-sm">
          <span className="text-xs text-muted-foreground">{s.label}</span>
          <span className="font-semibold tabular-nums">{s.value}</span>
        </span>
      )
    }
    case "callout": {
      const s = spec as {
        kind: "callout"
        title?: string
        tone?: "info" | "warning" | "danger" | "success"
        body?: string
      }
      const tone = s.tone ?? "info"
      const palette: Record<string, string> = {
        info: "border-sky-500/40 bg-sky-500/10",
        warning: "border-amber-500/40 bg-amber-500/10",
        danger: "border-rose-500/40 bg-rose-500/10",
        success: "border-emerald-500/40 bg-emerald-500/10",
      }
      return (
        <div className={`rounded-md border px-3 py-2 text-sm ${palette[tone]}`}>
          {s.title && <div className="mb-1 font-medium">{s.title}</div>}
          {s.body && <div className="text-muted-foreground">{s.body}</div>}
        </div>
      )
    }
    default:
      return (
        <pre className="rounded-md border border-border/60 bg-muted/40 p-2 text-[11px] font-mono text-muted-foreground">
          {JSON.stringify(spec, null, 2)}
        </pre>
      )
  }
}

export function MessageBody({ content, isToolResult, toolCalls }: MessageBodyProps) {
  const { prose, actionCount, cardBlocks } = useMemo(() => {
    const blocks = extractActionBlocks(content)
    const cleaned = stripToolCallTags(content)
      .replace(ACTION_BLOCK_RE, "")
      .trim()
    const { blocks: cardBlocks, stripped } = parseCardBlocks(cleaned)
    return {
      prose: stripped.trim(),
      actionCount: blocks.length,
      cardBlocks,
    }
  }, [content])

  if (isToolResult) {
    return (
      <div className="rounded-md border border-border/60 bg-muted/40 px-3 py-2 text-xs">
        <div className="mb-1 flex items-center gap-1.5 font-medium text-muted-foreground">
          <Wrench className="size-3" />
          Tool result
        </div>
        <pre className="overflow-x-auto whitespace-pre-wrap font-mono text-[11px] leading-snug text-foreground/80">
          {content}
        </pre>
      </div>
    )
  }

  return (
    <div className="prose prose-sm max-w-none dark:prose-invert">
      {prose && (
        <ReactMarkdown
          remarkPlugins={[remarkGfm]}
          components={{
            p: ({ children }) => <p className="my-1.5 leading-relaxed">{children}</p>,
            code: ({ children, className }) => {
              const isBlock = className?.startsWith("language-")
              if (isBlock) {
                return (
                  <pre className="my-2 overflow-x-auto rounded-md bg-muted p-3 text-xs">
                    <code className="font-mono">{children}</code>
                  </pre>
                )
              }
              return (
                <code className="rounded bg-muted px-1 py-0.5 font-mono text-[0.85em]">
                  {children}
                </code>
              )
            },
            ul: ({ children }) => <ul className="my-1.5 list-disc pl-5">{children}</ul>,
            ol: ({ children }) => <ol className="my-1.5 list-decimal pl-5">{children}</ol>,
            li: ({ children }) => <li className="my-0.5">{children}</li>,
            a: ({ children, href }) => (
              <a
                href={href}
                className="text-primary underline underline-offset-2"
                target="_blank"
                rel="noreferrer"
              >
                {children}
              </a>
            ),
            table: ({ children }) => (
              <div className="my-2 overflow-x-auto rounded-md border">
                <table className="w-full text-sm">{children}</table>
              </div>
            ),
            thead: ({ children }) => (
              <thead className="bg-muted/50 text-xs text-muted-foreground">{children}</thead>
            ),
            th: ({ children }) => (
              <th className="px-3 py-2 text-left font-medium">{children}</th>
            ),
            td: ({ children }) => <td className="border-t px-3 py-2">{children}</td>,
            input: ({ checked, type, ...rest }) =>
              type === "checkbox" ? (
                <input
                  type="checkbox"
                  checked={!!checked}
                  readOnly
                  {...rest}
                  className="mr-1.5 align-middle"
                />
              ) : (
                <input type={type} {...rest} />
              ),
          }}
        >
          {prose}
        </ReactMarkdown>
      )}
      {cardBlocks.length > 0 && (
        <div className="mt-2 flex flex-wrap items-center gap-2">
          {cardBlocks.map((spec, i) => (
            <span key={i} className={spec.kind === "callout" ? "block w-full" : ""}>
              {renderCard(spec)}
            </span>
          ))}
        </div>
      )}
      {actionCount > 0 && (
        <span
          className="mt-1 inline-flex items-center gap-1.5 rounded-full border border-primary/40 bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-primary dark:border-sky-400/60 dark:bg-sky-400/15 dark:text-sky-200"
          title="Action block executed by the command bus"
        >
          <Sparkles className="size-3" />
          Ran {actionCount} action{actionCount > 1 ? "s" : ""}
        </span>
      )}
      {toolCalls && toolCalls.length > 0 && (
        <span
          className="mt-1 ml-1 inline-flex items-center gap-1.5 rounded-full border border-amber-400/40 bg-amber-400/10 px-2.5 py-0.5 text-xs font-medium text-amber-700 dark:border-amber-300/60 dark:bg-amber-300/10 dark:text-amber-200"
          title="Tool call dispatched"
        >
          <Wrench className="size-3" />
          Called {toolCalls.map((c) => c.name).join(", ")}
        </span>
      )}
    </div>
  )
}
