import { createHash } from "node:crypto";
import { describe, expect, it } from "vitest";
import { DEFAULT_LAYERS, dedup } from "../dedup.js";

describe("dedup", () => {
  it("keeps the highest-score representative on nodeId collision", () => {
    const hits = [
      { nodeId: "a", score: 0.5, properties: {} },
      { nodeId: "a", score: 0.9, properties: {} },
    ];
    const out = dedup(hits);
    expect(out).toHaveLength(1);
    expect(out[0].score).toBe(0.9);
  });

  it("collapses slug collisions across distinct nodeIds", () => {
    const hits = [
      { nodeId: "a", score: 0.9, properties: { slug: "adam-langley" } },
      { nodeId: "b", score: 0.6, properties: { slug: "adam-langley" } },
    ];
    const out = dedup(hits);
    expect(out.map((h) => h.nodeId)).toEqual(["a"]);
  });

  it("collapses canonicalName collisions case-insensitively, falls back to name", () => {
    const hits = [
      { nodeId: "a", score: 0.9, properties: { canonicalName: "Acme Co" } },
      { nodeId: "b", score: 0.6, properties: { name: "acme co" } },
    ];
    const out = dedup(hits);
    expect(out.map((h) => h.nodeId)).toEqual(["a"]);
  });

  it("collapses contentHash collisions (compiledTruth wins over content)", () => {
    const hits = [
      { nodeId: "a", score: 0.9, properties: { compiledTruth: "X" } },
      { nodeId: "b", score: 0.6, properties: { content: "X" } },
    ];
    const out = dedup(hits);
    // hash("X") matches across both → "a" wins by score.
    expect(out.map((h) => h.nodeId)).toEqual(["a"]);
    const h = createHash("sha256").update("X").digest("hex").slice(0, 16);
    expect(h).toBeDefined();
  });

  it("skips a layer for hits missing its key — no false collision", () => {
    const hits = [
      { nodeId: "a", score: 0.9, properties: {} },
      { nodeId: "b", score: 0.6, properties: {} },
    ];
    const out = dedup(hits);
    expect(out.map((h) => h.nodeId)).toEqual(["a", "b"]);
  });

  it("honours layer subset", () => {
    const hits = [
      { nodeId: "a", score: 0.9, properties: { slug: "same" } },
      { nodeId: "b", score: 0.6, properties: { slug: "same" } },
    ];
    // nodeId-only dedup keeps both because nodeIds differ.
    const out = dedup(hits, ["nodeId"]);
    expect(out).toHaveLength(2);
  });

  it("preserves descending-score order in the output", () => {
    const hits = [
      { nodeId: "a", score: 0.3, properties: {} },
      { nodeId: "b", score: 0.9, properties: {} },
      { nodeId: "c", score: 0.6, properties: {} },
    ];
    const out = dedup(hits);
    expect(out.map((h) => h.score)).toEqual([0.9, 0.6, 0.3]);
  });

  it("exports the default-layers constant for reference", () => {
    expect(DEFAULT_LAYERS).toEqual([
      "nodeId",
      "slug",
      "canonicalName",
      "contentHash",
    ]);
  });
});
