// src/lib/backlink/normalize.test.ts
import { describe, expect, test } from "bun:test";
import { dedupeMerge, domainNorm, normalizeUrl } from "./normalize";
import type { LinkSourceSeed } from "./types";

const base = (over: Partial<LinkSourceSeed>): LinkSourceSeed => ({
  id: "x",
  platform: "X",
  url: "https://x.com",
  domainNorm: "x.com",
  kind: "directory",
  da: null,
  dr: null,
  effort: "medium",
  linkType: "nofollow",
  language: "en",
  themes: [],
  tags: [],
  flags: { toxic: false, disavow: false },
  ...over,
});

describe("normalizeUrl", () => {
  test("forces https and strips trailing slash", () => {
    expect(normalizeUrl("http://behance.net/")).toBe("https://behance.net");
  });
  test("lowercases the host but keeps the path", () => {
    expect(normalizeUrl("https://Biggerpockets.com")).toBe(
      "https://biggerpockets.com",
    );
    expect(normalizeUrl("https://news.ycombinator.com/showhn.html")).toBe(
      "https://news.ycombinator.com/showhn.html",
    );
  });
  test("returns empty string for blank input", () => {
    expect(normalizeUrl("")).toBe("");
    expect(normalizeUrl("   ")).toBe("");
  });
});

describe("domainNorm", () => {
  test("drops www and lowercases", () => {
    expect(domainNorm("https://www.ProductHunt.com/")).toBe("producthunt.com");
  });
  test("keeps subdomains other than www", () => {
    expect(domainNorm("http://news.ycombinator.com/showhn.html")).toBe(
      "news.ycombinator.com",
    );
  });
});

describe("dedupeMerge", () => {
  test("merges same domainNorm keeping the highest DA and union of tags", () => {
    const a = base({ id: "a", domainNorm: "dup.com", da: 40, tags: ["x"] });
    const b = base({ id: "b", domainNorm: "dup.com", da: 80, tags: ["y"] });
    const { sources, merges } = dedupeMerge([a, b]);
    expect(sources).toHaveLength(1);
    expect(sources[0].da).toBe(80);
    expect(sources[0].tags.sort()).toEqual(["x", "y"]);
    expect(merges).toHaveLength(1);
    expect(merges[0].domainNorm).toBe("dup.com");
  });
  test("treats null DA as lower than any number", () => {
    const a = base({ id: "a", domainNorm: "d.com", da: null });
    const b = base({ id: "b", domainNorm: "d.com", da: 10 });
    const { sources } = dedupeMerge([a, b]);
    expect(sources[0].da).toBe(10);
  });
  test("leaves distinct domains untouched", () => {
    const { sources, merges } = dedupeMerge([
      base({ id: "a", domainNorm: "a.com" }),
      base({ id: "b", domainNorm: "b.com" }),
    ]);
    expect(sources).toHaveLength(2);
    expect(merges).toHaveLength(0);
  });
  test("uses a custom key to keep same-domain distinct paths separate", () => {
    const a = base({
      id: "a",
      domainNorm: "reddit.com",
      url: "https://reddit.com/r/saas",
    });
    const b = base({
      id: "b",
      domainNorm: "reddit.com",
      url: "https://reddit.com/r/startups",
    });
    const { sources, merges } = dedupeMerge([a, b], (s) => s.url);
    expect(sources).toHaveLength(2);
    expect(merges).toHaveLength(0);
  });
});
