import { describe, expect, test } from "bun:test";
import {
  localSlug,
  normalizePageUrl,
  type PageRef,
  textualMatch,
} from "./mapping";

describe("localSlug", () => {
  test("lowercase, accents strippés, non-alphanum → '-', trim", () => {
    expect(localSlug("Bagué OR")).toBe("bague-or");
    expect(localSlug("  Éléphant & Co !  ")).toBe("elephant-co");
    expect(localSlug("déjà-vu")).toBe("deja-vu");
  });
  test("runs de séparateurs collapsés", () => {
    expect(localSlug("a   b---c")).toBe("a-b-c");
  });
});

describe("normalizePageUrl", () => {
  test("wrappe normalizeUrl (host lowercase, trailing slash strippé)", () => {
    expect(normalizePageUrl("HTTPS://Ex.COM/Path/")).toBe(
      "https://ex.com/Path",
    );
  });
});

describe("textualMatch", () => {
  const pages: PageRef[] = [
    {
      id: "p1",
      url: "https://ex.com/bague-or",
      title: null,
      h1: null,
    },
    {
      id: "p2",
      url: "https://ex.com/collection/bague-or",
      title: "Nos bagues",
      h1: "Bague or 18 carats",
    },
  ];

  test("slug dans path seul (40) < seuil → null", () => {
    const res = textualMatch("bague or", [pages[0]]);
    expect(res).toBeNull();
  });

  test("path + h1 (70) ≥ seuil → match", () => {
    const res = textualMatch("bague or", [pages[1]]);
    expect(res).toEqual({ pageId: "p2", score: 70 });
  });

  test("h1 + title sans path (60) → match", () => {
    const res = textualMatch("bague or", [
      {
        id: "x",
        url: "https://ex.com/collier",
        title: "Bague or",
        h1: "Bague or",
      },
    ]);
    expect(res).toEqual({ pageId: "x", score: 60 });
  });

  test("insensible aux accents et à la casse (« bagué OR » vs slug/h1 sans accent)", () => {
    const res = textualMatch("Bagué OR", [pages[1]]);
    expect(res).toEqual({ pageId: "p2", score: 70 });
  });

  test("aucune page candidate → null", () => {
    const res = textualMatch("montre argent", pages);
    expect(res).toBeNull();
  });

  test("meilleur score l'emporte", () => {
    const res = textualMatch("bague or", [
      { id: "low", url: "https://ex.com/bague-or", title: null, h1: null }, // 40 < seuil
      {
        id: "high",
        url: "https://ex.com/bague-or-18",
        title: "Bague or",
        h1: "Bague or",
      }, // 100
    ]);
    expect(res?.pageId).toBe("high");
    expect(res?.score).toBe(100);
  });

  test("égalité de score → URL la plus courte", () => {
    const res = textualMatch("bague or", [
      {
        id: "long",
        url: "https://ex.com/collection/premium/bague-or",
        title: null,
        h1: "Bague or",
      }, // 70
      {
        id: "short",
        url: "https://ex.com/bague-or",
        title: null,
        h1: "Bague or",
      }, // 70
    ]);
    expect(res?.pageId).toBe("short");
    expect(res?.score).toBe(70);
  });

  test("liste vide → null", () => {
    expect(textualMatch("quoi", [])).toBeNull();
  });

  // — Frontières de mot (fin des faux positifs sur termes courts) —

  test("« or » ne matche PAS le path /portfolio (token ≠ sous-chaîne)", () => {
    const res = textualMatch("or", [
      { id: "pf", url: "https://ex.com/portfolio", title: null, h1: null },
    ]);
    expect(res).toBeNull();
  });

  test("« or » ne matche PAS « bonjour » dans le H1 (pas de +30)", () => {
    const res = textualMatch("or", [
      {
        id: "b",
        url: "https://ex.com/accueil",
        title: "corps et âme",
        h1: "bonjour tout le monde",
      },
    ]);
    expect(res).toBeNull();
  });

  test("« bague or » matche le path /bague-or-blanc (tokens contigus → +40)", () => {
    const res = textualMatch("bague or", [
      {
        id: "bo",
        url: "https://ex.com/bague-or-blanc",
        title: "Bague or blanc",
        h1: null,
      },
    ]);
    // path (+40) + title (+30) = 70
    expect(res).toEqual({ pageId: "bo", score: 70 });
  });

  test("« creation » (foldé) matche le H1 « Création de bijoux »", () => {
    const res = textualMatch("creation", [
      {
        id: "c",
        url: "https://ex.com/atelier",
        title: null,
        h1: "Création de bijoux",
      },
    ]);
    expect(res).toBeNull(); // 30 seul < seuil : on vérifie le +30 via un cas combiné
  });

  test("« creation » (foldé) : +30 H1 sur « Création de bijoux » (avec path)", () => {
    const res = textualMatch("creation", [
      {
        id: "c",
        url: "https://ex.com/creation",
        title: null,
        h1: "Création de bijoux",
      },
    ]);
    // path (+40) + h1 (+30) = 70
    expect(res).toEqual({ pageId: "c", score: 70 });
  });

  test("séquence NON contiguë : « bague argent » vs « bague en or et argent » → pas de +30", () => {
    // Les mots « bague » et « argent » sont présents mais séparés par « en or et » :
    // le match token-à-token exige une séquence CONTIGUË → pas de match H1/title.
    const res = textualMatch("bague argent", [
      {
        id: "na",
        url: "https://ex.com/collier",
        title: "Bague en or et argent",
        h1: "Bague en or et argent",
      },
    ]);
    expect(res).toBeNull();
  });

  test("terme d'un seul caractère → jamais de match parasite", () => {
    // « e » apparaît en sous-chaîne dans « telephone » (path, h1, title) mais
    // n'est jamais un token isolé : aucun point ne doit tomber.
    const res = textualMatch("e", [
      {
        id: "t",
        url: "https://ex.com/telephone",
        title: "telephone",
        h1: "telephone",
      },
    ]);
    expect(res).toBeNull();
  });
});
