"use client";

import { ThinkingIndicator } from "@/components/ThinkingIndicator";
import { Button } from "@/components/ui/Button";
import { cn } from "@/lib/ui/variants";
import type { AiMessageView } from "@/lib/api/types";
import { bubbleSide, type AcceptAction } from "@/lib/workbench/aiConversation";
import {
  isPendingView,
  isStreamingView,
} from "@/lib/workbench/inlineConversation";

// 一组对话气泡（作者右 / AI 左）。抽屉与内联面板共用同一渲染，外观完全一致（DRY）。
// 对已落库的 ai 产出气泡挂「再接受」（复用编辑器既有 DRAFT-only HITL 回调，不绕验收事务）；
// clarify 气泡与进行中（未落库/流式）气泡不挂。
export const ACCEPT_LABEL: Record<AcceptAction, string> = {
  replace_chapter: "接受这版（替换整章）",
  append_draft: "插入正文（章末）",
  refill_segment: "回填选段",
};

interface ConversationThreadProps {
  messages: readonly AiMessageView[];
  // 本线程可执行的接受动作（据 thread.kind 推导）；null 时全部气泡不挂接受。
  action: AcceptAction | null;
  onAccept: (action: AcceptAction, m: AiMessageView) => void;
}

export function ConversationThread({
  messages,
  action,
  onAccept,
}: ConversationThreadProps) {
  return (
    <div className="space-y-2">
      {messages.map((m) => (
        <Bubble key={m.id} message={m} action={action} onAccept={onAccept} />
      ))}
    </div>
  );
}

interface BubbleProps {
  message: AiMessageView;
  action: AcceptAction | null;
  onAccept: (action: AcceptAction, m: AiMessageView) => void;
}

// 单条气泡：作者右（cinnabar-wash）/ AI 左（border+bg）；clarify ai 气泡额外渲染只读方向选项。
// 进行中气泡（meta._pending）不挂再接受；流式且空正文时渲染「生成中」指示（meta._streaming）。
function Bubble({ message, action, onAccept }: BubbleProps) {
  const side = bubbleSide(message.role);
  const options = message.kind === "clarify" ? readOptions(message.meta) : [];
  const pending = isPendingView(message);
  const showThinking = isStreamingView(message) && message.content.length === 0;
  // 只有已落库 ai 产出气泡（左侧、非 clarify）挂再接受；进行中气泡（仍在生成）不挂。
  const showAccept =
    action !== null &&
    side === "left" &&
    message.kind !== "clarify" &&
    !pending;
  return (
    <div
      className={cn("flex", side === "right" ? "justify-end" : "justify-start")}
    >
      <div
        className={cn(
          "max-w-[85%] rounded border px-3 py-2",
          side === "right"
            ? "border-cinnabar/30 bg-[var(--color-cinnabar-wash)]"
            : "border-line bg-bg",
          pending && "opacity-90",
        )}
      >
        {showThinking ? (
          <ThinkingIndicator label="生成中" className="text-sm text-cinnabar" />
        ) : (
          <p className="max-h-56 overflow-y-auto whitespace-pre-wrap text-sm text-ink">
            {message.content}
          </p>
        )}
        {options.length > 0 ? (
          <ul className="mt-2 flex flex-wrap gap-1.5">
            {options.map((opt, i) => (
              <li
                key={i}
                className="rounded border border-line bg-panel px-2 py-0.5 text-2xs text-ink-soft"
              >
                {opt}
              </li>
            ))}
          </ul>
        ) : null}
        {showAccept && action ? (
          <Button
            onClick={() => onAccept(action, message)}
            variant="secondary"
            size="sm"
            className="mt-2"
          >
            {ACCEPT_LABEL[action]}
          </Button>
        ) : null}
      </div>
    </div>
  );
}

// clarify ai 行 meta.options → 只读选项 label 列表（外部数据，逐项显式取值，不信任形状）。
function readOptions(meta: AiMessageView["meta"]): string[] {
  const raw = (meta ?? {})["options"];
  if (!Array.isArray(raw)) return [];
  return raw
    .map((o) =>
      o && typeof o === "object" && "label" in o
        ? String((o as { label: unknown }).label ?? "")
        : "",
    )
    .filter((label) => label.length > 0);
}
