import { describe, expect, it } from "vitest";
import { buildProjectColorMap, getProjectColor, getProjectHex } from "./project-colors.js";

// The palette has exactly 16 entries — exported indirectly through the type.
const PALETTE_SIZE = 16;

describe("getProjectColor", () => {
  it("returns an object with bg, dark, border, and hex fields", () => {
    const color = getProjectColor("proj-1");
    expect(color).toHaveProperty("bg");
    expect(color).toHaveProperty("dark");
    expect(color).toHaveProperty("border");
    expect(color).toHaveProperty("hex");
  });

  it("is deterministic — same ID always yields the same color", () => {
    const id = "stable-id-abc";
    const a = getProjectColor(id);
    const b = getProjectColor(id);
    expect(a).toBe(b); // same object reference from the const palette
  });

  it("returns different colors for different IDs (at least across a sample)", () => {
    const colors = new Set(
      Array.from({ length: 20 }, (_, i) => getProjectColor(`project-${i}`).hex),
    );
    // With 20 IDs spread across 16 slots we expect several distinct colors
    expect(colors.size).toBeGreaterThan(1);
  });

  it("handles an empty string ID without throwing", () => {
    expect(() => getProjectColor("")).not.toThrow();
  });

  it("handles a very long ID without throwing", () => {
    const longId = "x".repeat(1000);
    expect(() => getProjectColor(longId)).not.toThrow();
  });

  it("returned hex value starts with '#'", () => {
    const { hex } = getProjectColor("test-id");
    expect(hex).toMatch(/^#[0-9a-fA-F]{6}$/);
  });

  it("cycles through all 16 palette slots given enough IDs", () => {
    // Generate enough IDs to guarantee every palette slot is hit
    const hexSet = new Set<string>();
    for (let i = 0; i < 200; i++) {
      hexSet.add(getProjectColor(`id-collision-test-${i}`).hex);
    }
    expect(hexSet.size).toBe(PALETTE_SIZE);
  });

  it("bg class contains a valid Tailwind color segment", () => {
    const { bg } = getProjectColor("tailwind-check");
    expect(bg).toMatch(/^bg-[a-z]+-500\/70$/);
  });

  it("border class follows expected pattern", () => {
    const { border } = getProjectColor("border-check");
    expect(border).toMatch(/^border-[a-z]+-600$/);
  });
});

describe("getProjectHex", () => {
  it("appends the default alpha suffix 'B3'", () => {
    const result = getProjectHex("proj-alpha");
    expect(result).toMatch(/^#[0-9a-fA-F]{6}B3$/);
  });

  it("appends a custom alpha suffix", () => {
    const result = getProjectHex("proj-alpha", "FF");
    expect(result).toMatch(/^#[0-9a-fA-F]{6}FF$/);
  });

  it("is deterministic for the same ID", () => {
    const a = getProjectHex("same-id");
    const b = getProjectHex("same-id");
    expect(a).toBe(b);
  });

  it("returns a different hex when a different alpha is supplied", () => {
    const withDefault = getProjectHex("id-x");
    const withCustom = getProjectHex("id-x", "00");
    expect(withDefault).not.toBe(withCustom);
    // The base hex part should be the same
    expect(withDefault.slice(0, 7)).toBe(withCustom.slice(0, 7));
  });
});

describe("buildProjectColorMap", () => {
  it("returns a Map with an entry for each ID", () => {
    const ids = ["proj-a", "proj-b", "proj-c"];
    const map = buildProjectColorMap(ids);
    expect(map.size).toBe(3);
    for (const id of ids) {
      expect(map.has(id)).toBe(true);
    }
  });

  it("returns an empty Map for an empty array", () => {
    const map = buildProjectColorMap([]);
    expect(map.size).toBe(0);
  });

  it("each entry equals the direct getProjectColor result", () => {
    const ids = ["x", "y", "z"];
    const map = buildProjectColorMap(ids);
    for (const id of ids) {
      expect(map.get(id)).toBe(getProjectColor(id));
    }
  });

  it("handles duplicate IDs — last write wins (same color anyway)", () => {
    const map = buildProjectColorMap(["dup", "dup", "dup"]);
    expect(map.size).toBe(1);
    expect(map.get("dup")).toBe(getProjectColor("dup"));
  });

  it("does not mutate the input array", () => {
    const ids = ["a", "b"];
    const copy = [...ids];
    buildProjectColorMap(ids);
    expect(ids).toEqual(copy);
  });
});
