import { converter, formatHex } from "culori";
import wcagContrast from "wcag-contrast";
import type { PaletteRole, RenderResult, ThemePaletteBundle } from "./types.js";

const toOklch = converter("oklch");

// Dark variant OKLCH parameters: [L, C] per role
// Hue is extracted from seed and applied uniformly
const DARK_ROLES: Array<{ name: string; l: number; c: number }> = [
  { name: "background", l: 0.14, c: 0.010 },
  { name: "surface",    l: 0.17, c: 0.012 },
  { name: "overlay",    l: 0.22, c: 0.015 },
  { name: "text",       l: 0.93, c: 0.008 },
  { name: "accent-1",   l: 0.72, c: 0.15  },
  { name: "accent-2",   l: 0.65, c: 0.13  },
  { name: "accent-3",   l: 0.58, c: 0.10  },
];

// Light variant OKLCH parameters: [L, C] per role
const LIGHT_ROLES: Array<{ name: string; l: number; c: number }> = [
  { name: "background", l: 0.94, c: 0.005 },
  { name: "surface",    l: 0.91, c: 0.008 },
  { name: "overlay",    l: 0.85, c: 0.012 },
  { name: "text",       l: 0.28, c: 0.008 },
  { name: "accent-1",   l: 0.55, c: 0.16  },
  { name: "accent-2",   l: 0.48, c: 0.14  },
  { name: "accent-3",   l: 0.40, c: 0.11  },
];

/** Serialize OKLCH values to oklch(l c h) CSS string. */
function toOklchString(l: number, c: number, h: number): string {
  const rounded = (n: number, d = 4) => Math.round(n * 10 ** d) / 10 ** d;
  // oklch(lightness chroma hue) — perceptually uniform color space
  return `oklch(${rounded(l)} ${rounded(c)} ${rounded(h)})`;
}

function hexFromOklch(l: number, c: number, h: number): string {
  return formatHex({ mode: "oklch", l, c, h }) ?? "#000000";
}

/**
 * Build a 7-role palette from a single seed hex color.
 * All color math is done in OKLCH via culori. No intermediate color spaces used.
 * WCAG AA is validated per foreground/background pair.
 */
export function buildPalette(seedHex: string): PaletteRole[] {
  const oklch = toOklch(seedHex);
  if (!oklch) {
    throw new Error(`Invalid seed color: ${seedHex}`);
  }
  const hue = oklch.h ?? 0;

  // Pre-compute text hex values for WCAG checks
  const darkTextRole = DARK_ROLES.find((r) => r.name === "text")!;
  const lightTextRole = LIGHT_ROLES.find((r) => r.name === "text")!;
  const darkTextHex = hexFromOklch(darkTextRole.l, darkTextRole.c, hue);
  const lightTextHex = hexFromOklch(lightTextRole.l, lightTextRole.c, hue);

  return DARK_ROLES.map((darkRole, i) => {
    const lightRole = LIGHT_ROLES[i];

    const darkHex = hexFromOklch(darkRole.l, darkRole.c, hue);
    const lightHex = hexFromOklch(lightRole.l, lightRole.c, hue);

    const darkOklch = toOklchString(darkRole.l, darkRole.c, hue);
    const lightOklch = toOklchString(lightRole.l, lightRole.c, hue);

    // WCAG AA: text role is always true; other roles checked against text hex
    let darkWcagAA: boolean;
    let lightWcagAA: boolean;

    if (darkRole.name === "text") {
      darkWcagAA = true;
      lightWcagAA = true;
    } else {
      const darkRatio = wcagContrast.hex(darkHex, darkTextHex);
      darkWcagAA = darkRatio >= 4.5;

      const lightRatio = wcagContrast.hex(lightHex, lightTextHex);
      lightWcagAA = lightRatio >= 4.5;
    }

    return {
      name: darkRole.name,
      dark: { oklch: darkOklch, hex: darkHex, wcagAA: darkWcagAA },
      light: { oklch: lightOklch, hex: lightHex, wcagAA: lightWcagAA },
    } satisfies PaletteRole;
  });
}

// CSS token mapping: role name → CSS custom property name
const CSS_TOKEN_MAP: Record<string, string> = {
  background: "--background",
  surface:    "--card",
  overlay:    "--secondary",
  text:       "--foreground",
  "accent-1": "--primary",
  "accent-2": "--accent",
  "accent-3": "--muted",
};

/**
 * Export palette to CSS custom properties in :root { ... } format.
 */
export function exportToCss(palette: PaletteRole[], variant: "dark" | "light"): string {
  const lines = palette.map((role) => {
    const token = CSS_TOKEN_MAP[role.name] ?? `--${role.name}`;
    const value = role[variant].oklch;
    return `  ${token}: ${value};`;
  });
  return `:root {\n${lines.join("\n")}\n}`;
}

/**
 * Export palette to a Tailwind CSS config snippet.
 */
export function exportToTailwind(palette: PaletteRole[]): string {
  const darkColors = Object.fromEntries(
    palette.map((role) => [role.name, role.dark.hex]),
  );
  const lightColors = Object.fromEntries(
    palette.map((role) => [role.name, role.light.hex]),
  );

  const config = {
    theme: {
      extend: {
        colors: {
          dark: darkColors,
          light: lightColors,
        },
      },
    },
  };

  return `module.exports = ${JSON.stringify(config, null, 2)};`;
}

/**
 * Export palette to VS Code workbench color customizations.
 */
export function exportToVSCode(palette: PaletteRole[]): string {
  const bg = palette.find((r) => r.name === "background");
  const surface = palette.find((r) => r.name === "surface");
  const text = palette.find((r) => r.name === "text");
  const accent1 = palette.find((r) => r.name === "accent-1");
  const accent2 = palette.find((r) => r.name === "accent-2");

  const colors: Record<string, string> = {
    "editor.background": bg?.dark.hex ?? "#000000",
    "editor.foreground": text?.dark.hex ?? "#ffffff",
    "activityBar.background": surface?.dark.hex ?? "#000000",
    "activityBar.foreground": text?.dark.hex ?? "#ffffff",
    "sideBar.background": surface?.dark.hex ?? "#000000",
    "sideBar.foreground": text?.dark.hex ?? "#ffffff",
    "statusBar.background": accent1?.dark.hex ?? "#000000",
    "statusBar.foreground": bg?.dark.hex ?? "#ffffff",
    "tab.activeBackground": bg?.dark.hex ?? "#000000",
    "tab.activeForeground": text?.dark.hex ?? "#ffffff",
    "titleBar.activeBackground": surface?.dark.hex ?? "#000000",
    "titleBar.activeForeground": text?.dark.hex ?? "#ffffff",
    "focusBorder": accent2?.dark.hex ?? "#000000",
  };

  return JSON.stringify(colors, null, 2);
}

/**
 * Export palette to structured JSON.
 */
export function exportToJson(palette: PaletteRole[]): string {
  return JSON.stringify({ palette, generated: new Date().toISOString() }, null, 2);
}

/**
 * Main render entry point. Accepts { seedHex } and returns a JSON buffer
 * containing a ThemePaletteBundle with the full palette and all 4 exports.
 */
export async function renderThemePalette(
  input: Record<string, unknown>,
): Promise<RenderResult> {
  const seedHex = input.seedHex;
  if (typeof seedHex !== "string" || !seedHex) {
    throw new Error("renderThemePalette: seedHex is required");
  }

  const palette = buildPalette(seedHex);

  const bundle: ThemePaletteBundle = {
    type: "theme-palette-bundle",
    seedHex,
    palette,
    exports: {
      css: exportToCss(palette, "dark"),
      tailwind: exportToTailwind(palette),
      vscode: exportToVSCode(palette),
      json: exportToJson(palette),
    },
  };

  const buffer = Buffer.from(JSON.stringify(bundle, null, 2), "utf-8");

  return {
    filename: `theme-palette-${seedHex.replace("#", "")}.json`,
    contentType: "application/json",
    buffer,
  };
}
