import { describe, expect, it } from "vitest";
import {
  normaliseHex,
  tagDotColour,
  tagChipTokens,
  luminance,
} from "./colour";

/** WCAG relative-luminance + contrast for asserting chip legibility in tests. */
function lum(hex: string): number {
  const n = hex.replace("#", "");
  const [r, g, b] = [0, 2, 4].map((i) => {
    const s = parseInt(n.slice(i, i + 2), 16) / 255;
    return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
  });
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function contrast(a: string, b: string): number {
  const [la, lb] = [lum(a), lum(b)];
  const [hi, lo] = la >= lb ? [la, lb] : [lb, la];
  return (hi + 0.05) / (lo + 0.05);
}

describe("normaliseHex", () => {
  it("lowercases, hashes, and expands 3-digit hex", () => {
    expect(normaliseHex("  #FDE047 ")).toBe("#fde047");
    expect(normaliseHex("#FFF")).toBe("#ffffff");
  });
  it("returns null for an unparseable colour", () => {
    expect(normaliseHex("not-a-colour")).toBeNull();
  });
});

describe("tagDotColour", () => {
  it("returns the normalised hex", () => {
    expect(tagDotColour("#7C3AED")).toBe("#7c3aed");
  });
  it("falls back to slate for garbage input", () => {
    expect(tagDotColour("nope")).toBe("#64748b");
  });
});

describe("luminance", () => {
  it("ranks black below white", () => {
    expect(luminance("#000000")).toBeLessThan(luminance("#ffffff"));
  });
});

describe("tagChipTokens", () => {
  it("builds a soft tint, border, dot, and readable ink", () => {
    const t = tagChipTokens("#7c3aed");
    expect(t.dot).toBe("#7c3aed");
    // Both tint and border are blended over white, so lighter than the source;
    // the border carries more of the colour, so it sits between tint and source.
    expect(luminance(t.backgroundColor)).toBeGreaterThan(luminance("#7c3aed"));
    expect(luminance(t.borderColor)).toBeGreaterThan(luminance("#7c3aed"));
    expect(luminance(t.borderColor)).toBeLessThan(luminance(t.backgroundColor));
  });

  it("guarantees AA (≥4.5:1) ink for a hard, pale colour (yellow)", () => {
    const t = tagChipTokens("#fde047");
    expect(contrast(t.color, t.backgroundColor)).toBeGreaterThanOrEqual(4.5);
  });

  it("guarantees AA ink across a spread of freeform hues", () => {
    for (const c of ["#ef4444", "#22c55e", "#3b82f6", "#000000", "#ffffff", "#14163d"]) {
      const t = tagChipTokens(c);
      expect(contrast(t.color, t.backgroundColor)).toBeGreaterThanOrEqual(4.5);
    }
  });

  it("keeps a dark tag's ink close to its own hue (minimal darkening)", () => {
    // Navy is already dark enough — ink should not collapse to pure black.
    expect(tagChipTokens("#14163d").color).not.toBe("#000000");
  });

  it("falls back to a neutral chip for an unparseable colour", () => {
    const t = tagChipTokens("garbage");
    expect(t.dot).toBe("#64748b");
    expect(contrast(t.color, t.backgroundColor)).toBeGreaterThanOrEqual(4.5);
  });
});
