import path from 'node:path';
import { colord, extend } from 'colord';
import namesPlugin from 'colord/plugins/names';
import * as fse from 'fs-extra';
import postcss, { type Declaration, type Rule } from 'postcss';

extend([namesPlugin]);

/**
 * Converts any valid CSS color (hex, rgb(), hsl(), named colors, etc.)
 * into raw H S% L% format (no "hsl()" wrapper, just the values).
 * Example: "#f3f4f6" -> "220 13% 96%"
 * Example: "hsl(200 100% 95%)" -> "200 100% 95%"
 * Example: "rgb(180, 200, 220)" -> "210 35% 78%"
 */
export function convertColorToHslString(value: string): string {
  const c = colord(value);
  if (c.isValid()) {
    const { h, s, l } = c.toHsl();
    return `${h} ${s}% ${l}%`;
  }
  // Return the original value if it's not a valid color
  return value;
}

export interface ThemeVariables {
  light: Record<string, string>;
  dark: Record<string, string>;
}

/**
 * Parses CSS and extracts theme variables from :root (light) and .BU_dark-scheme (dark).
 */
export function parseThemeVariables(cssContent: string): ThemeVariables {
  const rootVars: Record<string, string> = {};
  const darkVars: Record<string, string> = {};

  const root = postcss.parse(cssContent);

  root.walkRules((rule: Rule) => {
    if (rule.selectors?.includes(':root')) {
      rule.walkDecls((decl: Declaration) => {
        if (decl.prop.startsWith('--')) {
          rootVars[decl.prop] = decl.value;
        }
      });
    } else if (rule.selectors?.includes('.BU_dark-scheme')) {
      rule.walkDecls((decl: Declaration) => {
        if (decl.prop.startsWith('--')) {
          darkVars[decl.prop] = decl.value;
        }
      });
    }
  });

  return { light: rootVars, dark: darkVars };
}

/**
 * Replaces variables in the given CSS content with new values (passed in via theme objects).
 */
function isColorValue(value: string): boolean {
  return colord(value).isValid();
}

export function replaceThemeVariables(
  cssContent: string,
  lightVars: Record<string, string>,
  darkVars: Record<string, string>,
): string {
  const root = postcss.parse(cssContent);

  root.walkRules((rule: Rule) => {
    if (rule.selectors?.includes(':root')) {
      rule.walkDecls((decl: Declaration) => {
        if (decl.prop.startsWith('--') && decl.prop in lightVars) {
          // @ts-expect-error - `value` is writable
          decl.value = lightVars[decl.prop];
        }
      });
    } else if (rule.selectors?.includes('.BU_dark-scheme')) {
      // Remove non-color variables from dark theme
      rule.walkDecls((decl: Declaration) => {
        if (!isColorValue(decl.value)) {
          throw new Error(
            `Dark theme contains non-color variable ${decl.prop}. Non-color variables should only be in :root`,
          );
        }
        if (decl.prop.startsWith('--') && decl.prop in darkVars) {
          // @ts-expect-error - `value` is writable
          decl.value = darkVars[decl.prop];
        }
      });
    }
  });

  return root.toString();
}

// File: src/validation.ts
/**
 * Ensures that each variable defined in the light theme has a counterpart in the dark theme
 * and vice versa. Throws an error if validation fails.
 */
export function validateVariables(
  lightVars: Record<string, string>,
  darkVars: Record<string, string>,
): void {
  const lightKeys = Object.keys(lightVars);
  const darkKeys = Object.keys(darkVars);

  // Check that all color variables in light theme have dark counterparts
  for (const [key, value] of Object.entries(lightVars)) {
    if (isColorValue(value)) {
      if (!darkKeys.includes(key)) {
        throw new Error(`Missing dark theme variable for color ${key}`);
      }
      if (value === darkVars[key]) {
        console.warn(
          `⚠️ Variable ${key} has the same value in both themes: ${value}`,
        );
      }
    }
  }

  // Check that dark theme only contains color variables
  for (const [key, value] of Object.entries(darkVars)) {
    if (!isColorValue(value)) {
      throw new Error(
        `Dark theme contains non-color variable ${key}. Non-color variables should only be in :root`,
      );
    }
    if (!lightKeys.includes(key)) {
      throw new Error(`Missing light theme variable for ${key}`);
    }
  }
}

// Add a constant for the generated file comment
const GENERATED_COMMENT =
  '/* This file was generated by the script at `packages/tooling/custom-scripts/scripts/css-theme-gen.ts`  */\n';

async function processCssFile(filePath: string): Promise<void> {
  try {
    const cssContent = await fse.readFile(filePath, 'utf-8');
    const { light, dark } = parseThemeVariables(cssContent);

    // Convert all values to HSL (raw format)
    const convertedLight: Record<string, string> = {};
    const convertedDark: Record<string, string> = {};

    validateVariables(light, dark);
    for (const [key, value] of Object.entries(light)) {
      convertedLight[key] = convertColorToHslString(value);
    }

    for (const [key, value] of Object.entries(dark)) {
      convertedDark[key] = convertColorToHslString(value);
    }

    // Validate

    // Replace in CSS
    const processedCss = replaceThemeVariables(
      cssContent,
      convertedLight,
      convertedDark,
    );

    // Prepend the generated comment
    const finalCss = GENERATED_COMMENT + processedCss;

    // Write output file
    const dir = path.dirname(filePath);
    const baseName = path.basename(filePath, path.extname(filePath));
    const outPath = path.join(dir, `${baseName}.processed.css`);

    await fse.writeFile(outPath, finalCss, 'utf-8');

    console.log(`✅ Processed CSS saved to ${outPath}`);
  } catch (error: any) {
    console.error(`❌ Error: ${error.message}`);
    process.exit(1);
  }
}

// If run directly (e.g. tsx src/index.ts input.css)
if (require.main === module) {
  const inputFile = process.argv[2];
  if (!inputFile) {
    console.error('❌ No input file specified.');
    process.exit(1);
  }
  void processCssFile(inputFile);
}
