import { describe, expect, it } from "vitest";
import {
  buildPalette,
  exportToCss,
  exportToJson,
  exportToTailwind,
  exportToVSCode,
  renderThemePalette,
} from "../services/renderers/theme-renderer.js";

const SEED_BLUE = "#1e66f5";
const SEED_RED = "#e64553";
const ROLE_NAMES = [
  "background",
  "surface",
  "overlay",
  "text",
  "accent-1",
  "accent-2",
  "accent-3",
];

describe("buildPalette", () => {
  it("returns exactly 7 PaletteRole objects", () => {
    const palette = buildPalette(SEED_BLUE);
    expect(palette).toHaveLength(7);
  });

  it("returns roles with correct names in order", () => {
    const palette = buildPalette(SEED_BLUE);
    expect(palette.map((r) => r.name)).toEqual(ROLE_NAMES);
  });

  it("each role has dark.oklch starting with 'oklch('", () => {
    const palette = buildPalette(SEED_BLUE);
    for (const role of palette) {
      expect(role.dark.oklch).toMatch(/^oklch\(/);
    }
  });

  it("each role has dark.hex starting with '#'", () => {
    const palette = buildPalette(SEED_BLUE);
    for (const role of palette) {
      expect(role.dark.hex).toMatch(/^#[0-9a-f]{6}$/i);
    }
  });

  it("each role has light.oklch starting with 'oklch('", () => {
    const palette = buildPalette(SEED_BLUE);
    for (const role of palette) {
      expect(role.light.oklch).toMatch(/^oklch\(/);
    }
  });

  it("each role has light.hex starting with '#'", () => {
    const palette = buildPalette(SEED_BLUE);
    for (const role of palette) {
      expect(role.light.hex).toMatch(/^#[0-9a-f]{6}$/i);
    }
  });

  it("dark background role has wcagAA: true (bg has high contrast against dark text)", () => {
    const palette = buildPalette(SEED_BLUE);
    const bg = palette.find((r) => r.name === "background")!;
    expect(bg.dark.wcagAA).toBe(true);
  });

  it("light background role has wcagAA: true (bg has high contrast against light text)", () => {
    const palette = buildPalette(SEED_BLUE);
    const bg = palette.find((r) => r.name === "background")!;
    expect(bg.light.wcagAA).toBe(true);
  });

  it("text role always has wcagAA: true in dark variant", () => {
    const palette = buildPalette(SEED_BLUE);
    const text = palette.find((r) => r.name === "text")!;
    expect(text.dark.wcagAA).toBe(true);
  });

  it("text role always has wcagAA: true in light variant", () => {
    const palette = buildPalette(SEED_BLUE);
    const text = palette.find((r) => r.name === "text")!;
    expect(text.light.wcagAA).toBe(true);
  });

  it("different seed hues produce different hex values", () => {
    const blueP = buildPalette(SEED_BLUE);
    const redP = buildPalette(SEED_RED);
    // Background hex should differ between seeds
    const blueBg = blueP.find((r) => r.name === "background")!.dark.hex;
    const redBg = redP.find((r) => r.name === "background")!.dark.hex;
    expect(blueBg).not.toBe(redBg);
  });

  it("different seed hues return same role names", () => {
    const palette = buildPalette(SEED_RED);
    expect(palette.map((r) => r.name)).toEqual(ROLE_NAMES);
  });

  it("no HSL values appear in any string field of the palette", () => {
    const palette = buildPalette(SEED_BLUE);
    const json = JSON.stringify(palette);
    expect(json.toLowerCase()).not.toContain("hsl");
  });
});

describe("exportToCss", () => {
  it("contains --background CSS custom property", () => {
    const palette = buildPalette(SEED_BLUE);
    const css = exportToCss(palette, "dark");
    expect(css).toContain("--background:");
  });

  it("contains --foreground CSS custom property", () => {
    const palette = buildPalette(SEED_BLUE);
    const css = exportToCss(palette, "dark");
    expect(css).toContain("--foreground:");
  });

  it("contains oklch( values", () => {
    const palette = buildPalette(SEED_BLUE);
    const css = exportToCss(palette, "dark");
    expect(css).toContain("oklch(");
  });

  it("wraps output in :root { ... }", () => {
    const palette = buildPalette(SEED_BLUE);
    const css = exportToCss(palette, "dark");
    expect(css).toContain(":root");
    expect(css).toContain("{");
    expect(css).toContain("}");
  });

  it("light variant contains different values than dark", () => {
    const palette = buildPalette(SEED_BLUE);
    const darkCss = exportToCss(palette, "dark");
    const lightCss = exportToCss(palette, "light");
    // The background values differ between dark and light
    expect(darkCss).not.toBe(lightCss);
  });
});

describe("exportToTailwind", () => {
  it("contains 'colors' key", () => {
    const palette = buildPalette(SEED_BLUE);
    const tw = exportToTailwind(palette);
    expect(tw).toContain("colors");
  });

  it("contains module.exports", () => {
    const palette = buildPalette(SEED_BLUE);
    const tw = exportToTailwind(palette);
    expect(tw).toContain("module.exports");
  });

  it("contains 'theme' key", () => {
    const palette = buildPalette(SEED_BLUE);
    const tw = exportToTailwind(palette);
    expect(tw).toContain("theme");
  });
});

describe("exportToVSCode", () => {
  it("contains editor.background key", () => {
    const palette = buildPalette(SEED_BLUE);
    const vscode = exportToVSCode(palette);
    expect(vscode).toContain("editor.background");
  });

  it("contains editor.foreground key", () => {
    const palette = buildPalette(SEED_BLUE);
    const vscode = exportToVSCode(palette);
    expect(vscode).toContain("editor.foreground");
  });

  it("is valid JSON", () => {
    const palette = buildPalette(SEED_BLUE);
    const vscode = exportToVSCode(palette);
    expect(() => JSON.parse(vscode)).not.toThrow();
  });
});

describe("exportToJson", () => {
  it("is parseable JSON", () => {
    const palette = buildPalette(SEED_BLUE);
    const json = exportToJson(palette);
    expect(() => JSON.parse(json)).not.toThrow();
  });

  it("parsed JSON contains palette array", () => {
    const palette = buildPalette(SEED_BLUE);
    const json = exportToJson(palette);
    const parsed = JSON.parse(json);
    expect(parsed).toHaveProperty("palette");
    expect(Array.isArray(parsed.palette)).toBe(true);
    expect(parsed.palette).toHaveLength(7);
  });

  it("parsed JSON contains generated ISO date", () => {
    const palette = buildPalette(SEED_BLUE);
    const json = exportToJson(palette);
    const parsed = JSON.parse(json);
    expect(parsed).toHaveProperty("generated");
    expect(typeof parsed.generated).toBe("string");
    // Should be an ISO date string
    expect(new Date(parsed.generated).toISOString()).toBe(parsed.generated);
  });
});

describe("renderThemePalette", () => {
  it("returns RenderResult with contentType application/json", async () => {
    const result = await renderThemePalette({ seedHex: SEED_BLUE });
    expect(result.contentType).toBe("application/json");
  });

  it("returns RenderResult with a filename", async () => {
    const result = await renderThemePalette({ seedHex: SEED_BLUE });
    expect(result.filename).toBeTruthy();
    expect(typeof result.filename).toBe("string");
  });

  it("returns RenderResult with a buffer", async () => {
    const result = await renderThemePalette({ seedHex: SEED_BLUE });
    expect(Buffer.isBuffer(result.buffer)).toBe(true);
  });

  it("buffer contains valid JSON with theme-palette-bundle type", async () => {
    const result = await renderThemePalette({ seedHex: SEED_BLUE });
    const parsed = JSON.parse(result.buffer.toString("utf-8"));
    expect(parsed.type).toBe("theme-palette-bundle");
    expect(parsed.seedHex).toBe(SEED_BLUE);
    expect(Array.isArray(parsed.palette)).toBe(true);
    expect(parsed.exports).toHaveProperty("css");
    expect(parsed.exports).toHaveProperty("tailwind");
    expect(parsed.exports).toHaveProperty("vscode");
    expect(parsed.exports).toHaveProperty("json");
  });

  it("throws an error when seedHex is missing", async () => {
    await expect(renderThemePalette({})).rejects.toThrow();
  });
});
