import { describe, expect, it } from "vitest";

import { darkTheme } from "../dark";
import { draculaTheme } from "../dracula";
import { lightTheme } from "../light";
import { nordTheme } from "../nord";
import {
  DEFAULT_THEME_ID,
  getAllThemes,
  getDarkThemes,
  getLightThemes,
  getTheme,
  getThemeOrDefault,
  isValidThemeId,
} from "../registry";
import { solarizedDarkTheme } from "../solarized-dark";
import type { ThemeColors, ThemeDefinition } from "../types";
import { themeToVariables } from "../types";

const ALL_THEMES = [darkTheme, lightTheme, nordTheme, draculaTheme, solarizedDarkTheme];

const REQUIRED_COLOR_KEYS: (keyof ThemeColors)[] = [
  "bg-950",
  "bg-900",
  "bg-850",
  "surface-800",
  "surface-750",
  "border-700",
  "text-100",
  "text-300",
  "text-500",
  "blue-500",
  "blue-400",
  "red-500",
  "red-400",
  "purple-500",
  "purple-400",
  "teal-500",
  "teal-400",
  "amber-500",
  "amber-400",
  "pink-500",
  "emerald-500",
  "orange-500",
  "cyan-500",
  "indigo-500",
];

describe("Theme Registry", () => {
  it("getAllThemes returns all 5 built-in themes", () => {
    const themes = getAllThemes();
    expect(themes).toHaveLength(5);
    expect(themes.map((t) => t.id)).toEqual(["dark", "light", "nord", "dracula", "solarized-dark"]);
  });

  it("getTheme returns correct theme by id", () => {
    expect(getTheme("dark")).toBe(darkTheme);
    expect(getTheme("light")).toBe(lightTheme);
    expect(getTheme("nord")).toBe(nordTheme);
    expect(getTheme("dracula")).toBe(draculaTheme);
    expect(getTheme("solarized-dark")).toBe(solarizedDarkTheme);
  });

  it("getTheme returns undefined for unknown id", () => {
    expect(getTheme("nonexistent")).toBeUndefined();
  });

  it("getThemeOrDefault falls back to dark theme", () => {
    expect(getThemeOrDefault("nonexistent")).toBe(darkTheme);
    expect(getThemeOrDefault("dark")).toBe(darkTheme);
  });

  it("getDarkThemes returns only dark themes", () => {
    const dark = getDarkThemes();
    expect(dark.every((t) => t.isDark)).toBe(true);
    expect(dark).toHaveLength(4);
  });

  it("getLightThemes returns only light themes", () => {
    const light = getLightThemes();
    expect(light.every((t) => !t.isDark)).toBe(true);
    expect(light).toHaveLength(1);
    expect(light[0]?.id).toBe("light");
  });

  it("isValidThemeId validates correctly", () => {
    expect(isValidThemeId("dark")).toBe(true);
    expect(isValidThemeId("light")).toBe(true);
    expect(isValidThemeId("nope")).toBe(false);
  });

  it("DEFAULT_THEME_ID is dark", () => {
    expect(DEFAULT_THEME_ID).toBe("dark");
  });

  it("getAllThemes returns a copy, not the internal array", () => {
    const a = getAllThemes();
    const b = getAllThemes();
    expect(a).not.toBe(b);
    expect(a).toEqual(b);
  });
});

describe("Theme Definitions", () => {
  it.each(ALL_THEMES.map((t) => [t.id, t] as const))(
    "%s has all required fields",
    (_id, theme: ThemeDefinition) => {
      expect(theme.id).toBeTruthy();
      expect(theme.name).toBeTruthy();
      expect(theme.description).toBeTruthy();
      expect(theme.author).toBeTruthy();
      expect(typeof theme.isDark).toBe("boolean");
      expect(theme.colorPreview).toHaveLength(5);
    }
  );

  it.each(ALL_THEMES.map((t) => [t.id, t] as const))(
    "%s has all required color tokens",
    (_id, theme: ThemeDefinition) => {
      for (const key of REQUIRED_COLOR_KEYS) {
        expect(theme.colors[key], `missing color: ${key}`).toBeTruthy();
        expect(theme.colors[key]).toMatch(/^#[0-9a-f]{6}$/i);
      }
    }
  );

  it.each(ALL_THEMES.map((t) => [t.id, t] as const))(
    "%s has valid shadow definitions",
    (_id, theme: ThemeDefinition) => {
      expect(theme.shadows.sm).toBeTruthy();
      expect(theme.shadows.md).toBeTruthy();
      expect(theme.shadows.lg).toBeTruthy();
    }
  );

  it.each(ALL_THEMES.map((t) => [t.id, t] as const))(
    "%s colorPreview values are valid hex colors",
    (_id, theme: ThemeDefinition) => {
      for (const color of theme.colorPreview) {
        expect(color).toMatch(/^#[0-9a-f]{6}$/i);
      }
    }
  );

  it("all theme IDs are unique", () => {
    const ids = ALL_THEMES.map((t) => t.id);
    expect(new Set(ids).size).toBe(ids.length);
  });
});

describe("themeToVariables", () => {
  it("maps color tokens to --ms-* CSS variables", () => {
    const vars = themeToVariables(darkTheme);
    expect(vars["--ms-bg-900"]).toBe("#0f141d");
    expect(vars["--ms-blue-500"]).toBe("#2f80ff");
    expect(vars["--ms-text-100"]).toBe("#eef3ff");
  });

  it("includes shadow variables", () => {
    const vars = themeToVariables(darkTheme);
    expect(vars["--shadow-sm"]).toBe(darkTheme.shadows.sm);
    expect(vars["--shadow-md"]).toBe(darkTheme.shadows.md);
    expect(vars["--shadow-lg"]).toBe(darkTheme.shadows.lg);
  });

  it("generates correct number of variables (24 colors + 3 shadows)", () => {
    const vars = themeToVariables(darkTheme);
    expect(Object.keys(vars)).toHaveLength(27);
  });
});
