import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, isAbsolute, join } from 'node:path';

const GREEN = '\x1b[32m';
const RED = '\x1b[31m';
const DIM = '\x1b[2m';
const CYAN = '\x1b[36m';
const RESET = '\x1b[0m';

export type ExportTarget = 'shadcn' | 'tailwind' | 'css-vars' | 'figma-tokens';

export interface ExportOptions {
  output?: string;
}

function readAdoptionMode(projectRoot: string): string | null {
  const projectJsonPath = join(projectRoot, '.decantr', 'project.json');
  if (!existsSync(projectJsonPath)) return null;
  try {
    const parsed = JSON.parse(readFileSync(projectJsonPath, 'utf-8')) as {
      initialized?: { adoptionMode?: unknown };
    };
    return typeof parsed.initialized?.adoptionMode === 'string'
      ? parsed.initialized.adoptionMode
      : null;
  } catch {
    return null;
  }
}

function resolveOutputPath(
  projectRoot: string,
  output: string | undefined,
  fallback: string,
): string {
  if (!output) return fallback;
  return isAbsolute(output) ? output : join(projectRoot, output);
}

// ── Token Mapping ──

export const SHADCN_MAP: Record<string, string> = {
  '--d-bg': '--background',
  '--d-text': '--foreground',
  '--d-surface': '--card',
  '--d-surface-raised': '--popover',
  '--d-primary': '--primary',
  '--d-secondary': '--secondary',
  '--d-accent': '--accent',
  '--d-border': '--border',
  '--d-text-muted': '--muted-foreground',
  '--d-error': '--destructive',
  '--d-radius': '--radius',
};

export const TAILWIND_COLOR_MAP: Record<string, string> = {
  '--d-primary': 'primary',
  '--d-secondary': 'secondary',
  '--d-accent': 'accent',
  '--d-bg': 'background',
  '--d-surface': 'surface',
  '--d-surface-raised': 'surface-raised',
  '--d-border': 'border',
  '--d-text': 'foreground',
  '--d-text-muted': 'muted',
  '--d-success': 'success',
  '--d-error': 'error',
  '--d-warning': 'warning',
  '--d-info': 'info',
};

// ── CSS Parsing ──

export function parseTokensCSS(css: string): Map<string, string> {
  const tokens = new Map<string, string>();
  const re = /(--d-[\w-]+)\s*:\s*([^;]+);/g;
  let match: RegExpExecArray | null;
  while ((match = re.exec(css)) !== null) {
    tokens.set(match[1], match[2].trim());
  }
  return tokens;
}

// ── Generators ──

export function generateShadcnCSS(tokens: Map<string, string>): string {
  const lines: string[] = [
    '/* Exported by decantr export --to shadcn */',
    '@layer base {',
    '  :root {',
  ];

  for (const [decantrVar, shadcnVar] of Object.entries(SHADCN_MAP)) {
    const value = tokens.get(decantrVar);
    if (value) {
      lines.push(`    ${shadcnVar}: ${value};`);
    }
  }

  // Map card-foreground and popover-foreground from text
  const textValue = tokens.get('--d-text');
  if (textValue) {
    lines.push(`    --card-foreground: ${textValue};`);
    lines.push(`    --popover-foreground: ${textValue};`);
  }

  // Map muted background from surface
  const surfaceValue = tokens.get('--d-surface');
  if (surfaceValue) {
    lines.push(`    --muted: ${surfaceValue};`);
  }

  // Map accent-foreground from text
  const accentFg = tokens.get('--d-text');
  if (accentFg) {
    lines.push(`    --accent-foreground: ${accentFg};`);
  }

  // Map destructive-foreground
  lines.push(`    --destructive-foreground: #FAFAFA;`);

  lines.push('  }', '}', '');
  return lines.join('\n');
}

export function generateShadcnComponentsJSON(): string {
  const config = {
    $schema: 'https://ui.shadcn.com/schema.json',
    style: 'default',
    rsc: false,
    tsx: true,
    tailwind: {
      config: 'tailwind.config.ts',
      css: 'src/styles/shadcn-theme.css',
      baseColor: 'zinc',
    },
    aliases: {
      components: '@/components',
      utils: '@/lib/utils',
    },
  };
  return JSON.stringify(config, null, 2) + '\n';
}

export function generateTailwindConfig(tokens: Map<string, string>): string {
  const colors: Record<string, string> = {};
  for (const [decantrVar, twName] of Object.entries(TAILWIND_COLOR_MAP)) {
    const value = tokens.get(decantrVar);
    if (value) {
      colors[twName] = value;
    }
  }

  const borderRadius: Record<string, string> = {};
  for (const suffix of ['', '-sm', '-lg', '-xl', '-full']) {
    const key = `--d-radius${suffix}`;
    const value = tokens.get(key);
    if (value) {
      const name = suffix ? suffix.slice(1) : 'DEFAULT';
      borderRadius[name] = value;
    }
  }

  const boxShadow: Record<string, string> = {};
  for (const suffix of ['-sm', '', '-md', '-lg']) {
    const key = `--d-shadow${suffix}`;
    const value = tokens.get(key);
    if (value) {
      const name = suffix ? suffix.slice(1) : 'DEFAULT';
      boxShadow[name] = value;
    }
  }

  const lines: string[] = [
    '// Generated by decantr export --to tailwind',
    "import type { Config } from 'tailwindcss';",
    '',
    'export default {',
    '  theme: {',
    '    extend: {',
  ];

  // Colors
  lines.push('      colors: {');
  for (const [name, value] of Object.entries(colors)) {
    lines.push(`        '${name}': '${value}',`);
  }
  lines.push('      },');

  // Border radius
  lines.push('      borderRadius: {');
  for (const [name, value] of Object.entries(borderRadius)) {
    lines.push(`        '${name}': '${value}',`);
  }
  lines.push('      },');

  // Box shadow
  lines.push('      boxShadow: {');
  for (const [name, value] of Object.entries(boxShadow)) {
    lines.push(`        '${name}': '${value}',`);
  }
  lines.push('      },');

  lines.push('    },', '  },', '} satisfies Partial<Config>;', '');
  return lines.join('\n');
}

export function generateCSSVars(tokens: Map<string, string>): string {
  const lines: string[] = ['/* Exported by decantr export --to css-vars */', ':root {'];

  for (const [key, value] of tokens) {
    lines.push(`  ${key}: ${value};`);
  }

  lines.push('}', '');
  return lines.join('\n');
}

export function generateFigmaTokens(tokens: Map<string, string>): string {
  const out: Record<string, { $type: string; $value: string; $description: string }> = {};

  for (const [key, value] of tokens) {
    const name = key.replace(/^--/, '').replace(/-/g, '.');
    const type =
      /color|bg|surface|text|border|primary|secondary|accent|error|warning|success|info/.test(key)
        ? 'color'
        : /radius/.test(key)
          ? 'borderRadius'
          : /shadow/.test(key)
            ? 'shadow'
            : /gap|space|spacing/.test(key)
              ? 'dimension'
              : 'string';
    out[name] = {
      $type: type,
      $value: value,
      $description: `Exported from ${key} by Decantr.`,
    };
  }

  return `${JSON.stringify(out, null, 2)}\n`;
}

// ── Main Command ──

export async function cmdExport(
  target: ExportTarget,
  projectRoot: string,
  options: ExportOptions = {},
): Promise<void> {
  const essencePath = join(projectRoot, 'decantr.essence.json');
  const tokensPath = join(projectRoot, 'src', 'styles', 'tokens.css');

  if (!existsSync(essencePath)) {
    console.error(`${RED}No decantr.essence.json found. Run \`decantr init\` first.${RESET}`);
    process.exitCode = 1;
    return;
  }

  if (!existsSync(tokensPath)) {
    const adoptionMode = readAdoptionMode(projectRoot);
    const message =
      adoptionMode === 'contract-only'
        ? `No src/styles/tokens.css found. This export reads Decantr CSS tokens, but this project is contract-only Brownfield and may intentionally use its own Tailwind/Sass token system. Export from the app's token source or adopt a style bridge before using \`decantr export --to ${target}\`.`
        : 'No src/styles/tokens.css found. Run `decantr refresh` to generate tokens.';
    console.error(`${RED}${message}${RESET}`);
    process.exitCode = 1;
    return;
  }

  const tokensCSS = readFileSync(tokensPath, 'utf-8');
  const tokens = parseTokensCSS(tokensCSS);

  if (tokens.size === 0) {
    console.error(`${RED}No --d-* tokens found in tokens.css.${RESET}`);
    process.exitCode = 1;
    return;
  }

  switch (target) {
    case 'shadcn': {
      const cssOut = resolveOutputPath(
        projectRoot,
        options.output,
        join(projectRoot, 'src', 'styles', 'shadcn-theme.css'),
      );
      const jsonOut = join(projectRoot, 'components.json');

      ensureDir(cssOut);
      writeFileSync(cssOut, generateShadcnCSS(tokens), 'utf-8');
      writeFileSync(jsonOut, generateShadcnComponentsJSON(), 'utf-8');

      console.log(`${GREEN}Exported shadcn theme:${RESET}`);
      console.log(`  ${DIM}CSS:${RESET}  ${cssOut}`);
      console.log(`  ${DIM}JSON:${RESET} ${jsonOut}`);
      break;
    }

    case 'tailwind': {
      const out = resolveOutputPath(
        projectRoot,
        options.output,
        join(projectRoot, 'tailwind.decantr.config.ts'),
      );

      ensureDir(out);
      writeFileSync(out, generateTailwindConfig(tokens), 'utf-8');

      console.log(`${GREEN}Exported Tailwind config:${RESET}`);
      console.log(`  ${DIM}File:${RESET} ${out}`);
      break;
    }

    case 'css-vars': {
      const out = resolveOutputPath(
        projectRoot,
        options.output,
        join(projectRoot, 'decantr-tokens.css'),
      );

      ensureDir(out);
      writeFileSync(out, generateCSSVars(tokens), 'utf-8');

      console.log(`${GREEN}Exported CSS variables:${RESET}`);
      console.log(`  ${DIM}File:${RESET} ${out}`);
      break;
    }

    case 'figma-tokens': {
      const out = resolveOutputPath(
        projectRoot,
        options.output,
        join(projectRoot, '.decantr', 'design', 'figma-tokens.json'),
      );

      ensureDir(out);
      writeFileSync(out, generateFigmaTokens(tokens), 'utf-8');

      console.log(`${GREEN}Exported Figma/Tokens Studio tokens:${RESET}`);
      console.log(`  ${DIM}File:${RESET} ${out}`);
      break;
    }
  }
}

function ensureDir(filePath: string): void {
  const dir = dirname(filePath);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }
}
