// src/lib/__tests__/design-tokens.test.ts
import { describe, it, expect } from "vitest";

/**
 * Asserts all token combinations meet WCAG contrast requirements for the
 * surfaces they actually render on. Rationale: outdoor + sun-glare is
 * harder than WCAG accounts for, so we aim for AAA (7:1) on the
 * surfaces text appears on, and AA (4.5:1) as the documented floor.
 *
 * Surface roles:
 *   bg        — page background
 *   surface   — card / panel background
 *   surface-2 — input fields, secondary cards
 *   surface-3 — high-emphasis containers, skeletons, dividers
 *
 * Token roles (and which assertion groups they belong to):
 *   text / text-muted       — body text, appears on all 4 surfaces → AAA
 *   text-faint              — meta / timestamps, appears on all 4 surfaces → AA
 *   accent / success        — brand + positive status text → AAA on bg, surface
 *   danger / warning / info — status text, on bg + surface + their -soft pill bg → AAA
 *   accent-2                — button background, white text sits on top → AAA
 *   *-soft                  — pill background, distinct from page surface (decorative)
 */

type RGB = [number, number, number];

function parseHex(hex: string): RGB {
  const h = hex.replace("#", "");
  return [
    parseInt(h.slice(0, 2), 16),
    parseInt(h.slice(2, 4), 16),
    parseInt(h.slice(4, 6), 16),
  ];
}

function srgbToLinear(c: number): number {
  const v = c / 255;
  return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
}

function luminance([r, g, b]: RGB): number {
  return (
    0.2126 * srgbToLinear(r) +
    0.7152 * srgbToLinear(g) +
    0.0722 * srgbToLinear(b)
  );
}

function contrastRatio(fg: string, bg: string): number {
  const l1 = luminance(parseHex(fg));
  const l2 = luminance(parseHex(bg));
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}

const SURFACES: Record<string, string> = {
  bg: "#ffffff",
  surface: "#faf8f5",
  "surface-2": "#f5f5f7",
  "surface-3": "#e8e8ed",
};

// Page surfaces that status text actually renders on. Status text does
// not render directly on --color-surface-3 (skeleton/dividers); it lives
// on its own -soft pill background instead.
const PAGE_SURFACES: Record<string, string> = {
  bg: "#ffffff",
  surface: "#faf8f5",
};

const BODY_TEXT_AAA: Record<string, string> = {
  text: "#1d1d1f",
  "text-muted": "#424245",
  info: "#1e3a8a",
};

const META_TEXT_AA: Record<string, string> = {
  "text-faint": "#5e5e63",
};

const STATUS_TEXT_AAA: Record<string, string> = {
  accent: "#14532d",
  danger: "#7f1d1d",
  warning: "#5e2a04",
  success: "#14532d",
};

const STATUS_ON_SOFT: Record<string, { text: string; soft: string }> = {
  "accent on accent-soft": { text: "#14532d", soft: "#dcfce7" },
  "danger on danger-soft": { text: "#7f1d1d", soft: "#fee2e2" },
  "warning on warning-soft": { text: "#5e2a04", soft: "#fef9c3" },
  "success on success-soft": { text: "#14532d", soft: "#dcfce7" },
  "info on info-soft": { text: "#1e3a8a", soft: "#dbeafe" },
};

const ON_ACCENT_BUTTON = {
  fg: "#ffffff", // button label
  bg: "#166534", // --color-accent-2
};

describe("design tokens — WCAG contrast", () => {
  describe("body text on every page surface (AAA)", () => {
    Object.entries(BODY_TEXT_AAA).forEach(([token, hex]) => {
      Object.entries(SURFACES).forEach(([surfaceName, surfaceHex]) => {
        it(`${token} on ${surfaceName} meets AAA (>= 7:1)`, () => {
          const ratio = contrastRatio(hex, surfaceHex);
          expect(
            ratio,
            `${token} (${hex}) on ${surfaceName} (${surfaceHex}) = ${ratio.toFixed(2)}:1`,
          ).toBeGreaterThanOrEqual(7);
        });
      });
    });
  });

  describe("meta text on every page surface (AA)", () => {
    Object.entries(META_TEXT_AA).forEach(([token, hex]) => {
      Object.entries(SURFACES).forEach(([surfaceName, surfaceHex]) => {
        it(`${token} on ${surfaceName} meets AA (>= 4.5:1)`, () => {
          const ratio = contrastRatio(hex, surfaceHex);
          expect(
            ratio,
            `${token} (${hex}) on ${surfaceName} (${surfaceHex}) = ${ratio.toFixed(2)}:1`,
          ).toBeGreaterThanOrEqual(4.5);
        });
      });
    });
  });

  describe("status text on page surfaces (AAA)", () => {
    Object.entries(STATUS_TEXT_AAA).forEach(([token, hex]) => {
      Object.entries(PAGE_SURFACES).forEach(([surfaceName, surfaceHex]) => {
        it(`${token} on ${surfaceName} meets AAA (>= 7:1)`, () => {
          const ratio = contrastRatio(hex, surfaceHex);
          expect(
            ratio,
            `${token} (${hex}) on ${surfaceName} (${surfaceHex}) = ${ratio.toFixed(2)}:1`,
          ).toBeGreaterThanOrEqual(7);
        });
      });
    });
  });

  describe("status text on its -soft pill background (AAA)", () => {
    Object.entries(STATUS_ON_SOFT).forEach(([name, { text, soft }]) => {
      it(`${name} meets AAA (>= 7:1)`, () => {
        const ratio = contrastRatio(text, soft);
        expect(
          ratio,
          `${name}: ${text} on ${soft} = ${ratio.toFixed(2)}:1`,
        ).toBeGreaterThanOrEqual(7);
      });
    });
  });

  describe("button text on accent-2 background (AAA)", () => {
    it(`white on accent-2 meets AAA (>= 7:1)`, () => {
      const ratio = contrastRatio(ON_ACCENT_BUTTON.fg, ON_ACCENT_BUTTON.bg);
      expect(
        ratio,
        `white on ${ON_ACCENT_BUTTON.bg} = ${ratio.toFixed(2)}:1`,
      ).toBeGreaterThanOrEqual(7);
    });
  });

  describe("pill backgrounds are visually distinct from page surface", () => {
    // The "soft" backgrounds are decorative. They don't need to pass
    // contrast on their own, but they must read as a distinct fill
    // against the page surface.
    const softs: Record<string, string> = {
      "accent-soft": "#dcfce7",
      "danger-soft": "#fee2e2",
      "warning-soft": "#fef9c3",
      "success-soft": "#dcfce7",
      "info-soft": "#dbeafe",
    };
    Object.entries(softs).forEach(([token, hex]) => {
      it(`${token} reads as a distinct fill on --color-surface`, () => {
        const ratio = contrastRatio(hex, "#faf8f5");
        expect(
          ratio,
          `${token} too close to surface (${ratio.toFixed(2)}:1)`,
        ).toBeLessThan(1.5);
      });
    });
  });
});
