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

import { diffWords } from "./ArticleBodyDiff";

/** Rebuild each side from the token stream — the diff must lose nothing. */
const rebuild = (
  tokens: ReturnType<typeof diffWords>,
  side: "before" | "after",
): string =>
  tokens
    .filter((t) =>
      side === "before" ? t.op !== "added" : t.op !== "removed",
    )
    .map((t) => t.text)
    .join("");

describe("diffWords", () => {
  it("marks only the words that actually changed", () => {
    const tokens = diffWords(
      "The carrier shall deliver within 30 days.",
      "The carrier shall deliver within 45 days.",
    );

    expect(tokens.filter((t) => t.op === "removed").map((t) => t.text)).toEqual([
      "30",
    ]);
    expect(tokens.filter((t) => t.op === "added").map((t) => t.text)).toEqual([
      "45",
    ]);
  });

  it("reconstructs both sides losslessly, whitespace included", () => {
    const before = "Payment is due\nwithin ten (10) working days.";
    const after = "Payment is due\nwithin five (5) working days of invoice.";
    const tokens = diffWords(before, after);

    expect(rebuild(tokens, "before")).toBe(before);
    expect(rebuild(tokens, "after")).toBe(after);
  });

  it("reports nothing changed for identical text", () => {
    const tokens = diffWords("Same clause.", "Same clause.");
    expect(tokens.every((t) => t.op === "same")).toBe(true);
  });

  it("handles a body being emptied or written from scratch", () => {
    expect(rebuild(diffWords("Some clause.", ""), "after")).toBe("");
    expect(rebuild(diffWords("", "Brand new clause."), "before")).toBe("");
  });

  it("falls back to a whole-block replace on pathological input", () => {
    // Past MAX_TOKENS the LCS table is skipped; the change must still be
    // reported truthfully rather than silently dropped.
    const before = Array.from({ length: 2000 }, (_, i) => `a${i}`).join(" ");
    const after = Array.from({ length: 2000 }, (_, i) => `b${i}`).join(" ");
    const tokens = diffWords(before, after);

    expect(rebuild(tokens, "before")).toBe(before);
    expect(rebuild(tokens, "after")).toBe(after);
  });
});
