import { describe, expect, it } from "vitest";

import type { AiMessageView } from "@/lib/api/types";
import {
  PENDING_CREATED_AT,
  isPendingView,
  isStreamingView,
  mergeInlineThreads,
  readSegment,
  toPendingView,
} from "./inlineConversation";

// AiMessageView 工厂：只填测试关心的字段，其余给稳定默认（对齐 aiConversation.test.ts）。
function view(partial: Partial<AiMessageView>): AiMessageView {
  return {
    id: partial.id ?? crypto.randomUUID(),
    project_id: "p1",
    chapter_no: partial.chapter_no ?? null,
    thread_id: partial.thread_id ?? "t1",
    seq: partial.seq ?? 0,
    kind: partial.kind ?? "refine",
    tool_key: partial.tool_key ?? null,
    role: partial.role ?? "ai",
    content: partial.content ?? "",
    meta: partial.meta ?? {},
    created_at: partial.created_at ?? "2026-07-09T00:00:00Z",
  };
}

describe("mergeInlineThreads", () => {
  it("只保留本章 + 指定 kinds 的已落库消息", () => {
    const persisted = [
      view({ thread_id: "a", chapter_no: 1, kind: "refine", seq: 0 }),
      view({ thread_id: "b", chapter_no: 2, kind: "refine", seq: 0 }), // 他章
      view({ thread_id: "c", chapter_no: 1, kind: "continue", seq: 0 }), // 他 kind
    ];
    const threads = mergeInlineThreads(persisted, 1, ["refine", "clarify"], []);
    expect(threads.map((t) => t.threadId)).toEqual(["a"]);
  });

  it("并入进行中气泡（按 id 去重、已落库优先），排在本章会话末尾", () => {
    const persisted = [
      view({ id: "p1", thread_id: "s", chapter_no: 1, kind: "refine", seq: 0, role: "author", content: "原文" }),
      view({ id: "p2", thread_id: "s", chapter_no: 1, kind: "refine", seq: 1, role: "ai", content: "第一版" }),
    ];
    const pending = [
      toPendingView({ localId: "pend-a", role: "author", content: "再改" }, { threadId: "s", chapterNo: 1, kind: "refine", seq: 0 }),
      toPendingView({ localId: "pend-b", role: "ai", content: "", streaming: true }, { threadId: "s", chapterNo: 1, kind: "refine", seq: 1 }),
    ];
    const threads = mergeInlineThreads(persisted, 1, ["refine"], pending);
    expect(threads).toHaveLength(1);
    expect(threads[0]!.messages.map((m) => m.id)).toEqual(["p1", "p2", "pend-a", "pend-b"]);
  });

  it("进行中气泡 id 与已落库重复时不重复（已落库优先）", () => {
    const persisted = [view({ id: "dup", thread_id: "s", chapter_no: 1, kind: "refine", seq: 0 })];
    const pending = [
      toPendingView({ localId: "dup", role: "ai", content: "x" }, { threadId: "s", chapterNo: 1, kind: "refine", seq: 9 }),
    ];
    const threads = mergeInlineThreads(persisted, 1, ["refine"], pending);
    expect(threads[0]!.messages).toHaveLength(1);
    expect(threads[0]!.messages[0]!.id).toBe("dup");
  });

  it("无已落库时进行中气泡自成一线程", () => {
    const pending = [
      toPendingView({ localId: "x", role: "author", content: "hi" }, { threadId: "new", chapterNo: 1, kind: "rewrite", seq: 0 }),
    ];
    const threads = mergeInlineThreads([], 1, ["rewrite"], pending);
    expect(threads).toHaveLength(1);
    expect(threads[0]!.messages[0]!.id).toBe("x");
  });

  it("空输入 → 空数组", () => {
    expect(mergeInlineThreads([], 1, ["refine"], [])).toEqual([]);
  });
});

describe("toPendingView", () => {
  it("合成未落库气泡：meta._pending，映射 role/content/kind/seq，created_at 恒为未来常量", () => {
    const v = toPendingView(
      { localId: "L1", role: "ai", content: "改写中", streaming: true },
      { threadId: "t9", chapterNo: 3, kind: "rewrite", seq: 2 },
    );
    expect(v.id).toBe("L1");
    expect(v.role).toBe("ai");
    expect(v.content).toBe("改写中");
    expect(v.thread_id).toBe("t9");
    expect(v.chapter_no).toBe(3);
    expect(v.kind).toBe("rewrite");
    expect(v.seq).toBe(2);
    expect(v.created_at).toBe(PENDING_CREATED_AT);
    expect(isPendingView(v)).toBe(true);
    expect(isStreamingView(v)).toBe(true);
  });

  it("非流式种子不带 _streaming", () => {
    const v = toPendingView(
      { localId: "L2", role: "author", content: "x" },
      { threadId: "t", chapterNo: 1, kind: "refine", seq: 0 },
    );
    expect(isStreamingView(v)).toBe(false);
    expect(isPendingView(v)).toBe(true);
  });
});

describe("isPendingView / isStreamingView", () => {
  it("已落库消息（无标记）均为 false", () => {
    const v = view({});
    expect(isPendingView(v)).toBe(false);
    expect(isStreamingView(v)).toBe(false);
  });
});

describe("readSegment", () => {
  it("取 meta.segment 字符串；缺失/非串回退空串", () => {
    expect(readSegment({ segment: "原选段" })).toBe("原选段");
    expect(readSegment({})).toBe("");
    expect(readSegment({ segment: 42 })).toBe("");
    expect(readSegment(undefined)).toBe("");
  });
});
