/**
 * Token generator — reads bytelyst.tokens.json, outputs 4 platform formats:
 *   1. CSS custom properties (tokens.css)
 *   2. TypeScript constants (tokens.ts)
 *   3. Kotlin object (MindLystTokens.kt) — for KMP shared module
 *   4. Swift structs (MindLystTheme.swift) — for iOS SwiftUI
 *
 * Output conventions match the hand-written originals in learning_multimodal_memory_agents:
 *   - Kotlin: SCREAMING_SNAKE_CASE, Palette/Dark/Light/BrainGradient/Typography/Motion/Layout
 *   - Swift:  Color(hex: UInt), dark/light prefixes, Gradient(colors:), MindLystMotion, Color ext
 *   - CSS:    [data-theme], --ml-fs-*, --ml-elevation-*, --ml-motion-*
 *
 * Usage: tsx scripts/generate.ts
 */

/* eslint-disable no-console -- This generator is a CLI; console output confirms generated artifacts. */

import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const tokensPath = resolve(__dirname, '../tokens/bytelyst.tokens.json');
const outDir = resolve(__dirname, '../generated');

mkdirSync(outDir, { recursive: true });

const tokens = JSON.parse(readFileSync(tokensPath, 'utf-8'));

// ── Product CSS mapping ─────────────────────────────────────────────
const PRODUCT_CSS_MAP: Record<string, { prefix: string; colorsKey: string }> = {
  mindlyst: { prefix: 'ml', colorsKey: 'brain' },
  chronomind: { prefix: 'cm', colorsKey: 'chronomind' },
  jarvisjr: { prefix: 'jj', colorsKey: 'jarvisjr' },
  nomgap: { prefix: 'ng', colorsKey: 'nomgap' },
  actiontrail: { prefix: 'at', colorsKey: 'actiontrail' },
  flowmonk: { prefix: 'fm', colorsKey: 'flowmonk' },
  notelett: { prefix: 'nl', colorsKey: 'notelett' },
  localmemgpt: { prefix: 'lmg', colorsKey: 'localmemgpt' },
  localllmlab: { prefix: 'llm', colorsKey: 'localllmlab' },
  lysnrai: { prefix: 'lys', colorsKey: 'lysnrai' },
  peakpulse: { prefix: 'pp', colorsKey: 'peakpulse' },
};

// ── Helpers ──────────────────────────────────────────────────────────

function camelToKebab(str: string): string {
  return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}

function camelToScreamingSnake(str: string): string {
  return str.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
}

function hexToUInt(hex: string): string {
  return `0x${hex.replace('#', '').toUpperCase()}`;
}

function capitalize(s: string): string {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

// ── 1. CSS ───────────────────────────────────────────────────────────
function generateCSS(): string {
  const lines: string[] = [
    '/* Auto-generated from bytelyst.tokens.json — do not edit manually */',
    '',
    ':root,',
    '[data-theme="dark"] {',
  ];

  // Semantic colors (dark theme as default)
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    lines.push(`  --ml-${camelToKebab(key)}: ${value};`);
  }
  lines.push('');

  // ByteLyst semantic aliases. These are the shared UI contract and map the
  // historical MindLyst token names into a product-neutral design system.
  lines.push('  --bl-bg-canvas: var(--ml-bg-canvas);');
  lines.push('  --bl-bg-elevated: var(--ml-bg-elevated);');
  lines.push('  --bl-surface-card: var(--ml-surface-card);');
  lines.push('  --bl-surface-muted: var(--ml-surface-muted);');
  lines.push('  --bl-surface-highlight: color-mix(in oklab, var(--ml-surface-muted) 82%, white);');
  lines.push('  --bl-surface-overlay: color-mix(in oklab, var(--ml-bg-canvas) 88%, transparent);');
  lines.push(
    '  --bl-input: color-mix(in oklab, var(--ml-surface-muted) 76%, var(--ml-bg-canvas));'
  );
  lines.push('  --bl-border: var(--ml-border-default);');
  lines.push('  --bl-border-strong: var(--ml-border-strong);');
  lines.push(
    '  --bl-border-subtle: color-mix(in oklab, var(--ml-border-default) 62%, transparent);'
  );
  lines.push('  --bl-text-primary: var(--ml-text-primary);');
  lines.push('  --bl-text-secondary: var(--ml-text-secondary);');
  lines.push('  --bl-text-tertiary: var(--ml-text-tertiary);');
  lines.push(
    '  --bl-text-quiet: color-mix(in oklab, var(--ml-text-secondary) 78%, var(--ml-bg-canvas));'
  );
  lines.push('  --bl-accent: var(--ml-accent-primary);');
  lines.push('  --bl-accent-foreground: var(--ml-bg-canvas);');
  lines.push(
    '  --bl-accent-muted: color-mix(in oklab, var(--ml-accent-primary) 16%, transparent);'
  );
  lines.push('  --bl-info: var(--ml-accent-primary);');
  lines.push('  --bl-info-muted: color-mix(in oklab, var(--ml-accent-primary) 14%, transparent);');
  lines.push('  --bl-success: var(--ml-success);');
  lines.push('  --bl-success-muted: color-mix(in oklab, var(--ml-success) 14%, transparent);');
  lines.push('  --bl-warning: var(--ml-warning);');
  lines.push('  --bl-warning-muted: color-mix(in oklab, var(--ml-warning) 14%, transparent);');
  lines.push('  --bl-danger: var(--ml-danger);');
  lines.push('  --bl-danger-muted: color-mix(in oklab, var(--ml-danger) 14%, transparent);');
  lines.push('  --bl-danger-foreground: var(--ml-bg-canvas);');
  lines.push('  --bl-focus-ring: var(--ml-focus-ring);');
  lines.push(
    '  --bl-focus-ring-muted: color-mix(in oklab, var(--ml-accent-primary) 18%, transparent);'
  );
  lines.push('  --bl-overlay-scrim: var(--ml-overlay-scrim);');
  lines.push('');

  // Typography
  for (const [key, value] of Object.entries(tokens.typography.fontFamily)) {
    // Swap single quotes → double quotes for CSS
    const cssVal = typeof value === 'string' ? value.replace(/'/g, '"') : value;
    lines.push(`  --ml-font-${key}: ${cssVal};`);
  }
  lines.push('');

  // Font sizes (--ml-fs-* to match existing convention)
  for (const [key, value] of Object.entries(tokens.typography.fontSize)) {
    lines.push(`  --ml-fs-${key}: ${value}px;`);
  }
  lines.push('');

  // Spacing — raw (--ml-*) tier, fixed pixel values
  for (const [key, value] of Object.entries(tokens.spacing)) {
    lines.push(`  --ml-space-${key}: ${value === 0 ? '0' : `${value}px`};`);
  }
  lines.push('');

  // Density-aware spacing tier (--bl-*) — added in design-tokens 0.2.0.
  // Each --bl-space-N = --ml-space-N × var(--bl-space-scale).
  // Consumers override --bl-space-scale via [data-density="..."] on <html>
  // (or any ancestor) to switch between compact / comfortable / spacious.
  lines.push('  /* Density scale — overridden by [data-density] selectors below */');
  lines.push('  --bl-space-scale: 1;');
  for (const [key, value] of Object.entries(tokens.spacing)) {
    if (value === 0) {
      lines.push(`  --bl-space-${key}: 0;`);
    } else {
      lines.push(`  --bl-space-${key}: calc(var(--ml-space-${key}) * var(--bl-space-scale));`);
    }
  }
  lines.push('');

  // Radius
  for (const [key, value] of Object.entries(tokens.radius)) {
    lines.push(`  --ml-radius-${key}: ${value}px;`);
  }
  lines.push('  --bl-radius-control: var(--ml-radius-xs);');
  lines.push('  --bl-radius-surface: var(--ml-radius-sm);');
  lines.push('  --bl-radius-card: var(--ml-radius-md);');
  lines.push('  --bl-radius-panel: var(--ml-radius-lg);');
  lines.push('  --bl-radius-pill: var(--ml-radius-pill);');
  lines.push('');

  // Elevation (--ml-elevation-* to match existing)
  for (const [key, value] of Object.entries(tokens.elevation)) {
    if (key === 'none') continue;
    lines.push(`  --ml-elevation-${key}: ${value};`);
  }
  lines.push('  --bl-shadow-sm: var(--ml-elevation-sm);');
  lines.push('  --bl-shadow-md: var(--ml-elevation-md);');
  lines.push('  --bl-shadow-lg: var(--ml-elevation-lg);');
  lines.push('');

  // Motion
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    if (key === 'instant') continue; // not used in CSS
    lines.push(`  --ml-motion-${key}: ${value}ms;`);
  }
  lines.push(`  --ml-easing-standard: ${tokens.motion.easing.standard};`);

  lines.push('}', '');

  // Light theme overrides
  lines.push('[data-theme="light"] {');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    // Only emit overrides where light differs from dark
    const darkVal = tokens.color.semantic.dark[key];
    if (value !== darkVal) {
      lines.push(`  --ml-${camelToKebab(key)}: ${value};`);
    }
  }
  lines.push('}', '');

  // Density overrides — drive --bl-space-scale via [data-density="..."].
  // Default (no attribute or `comfortable`) keeps scale = 1 from :root.
  // Added in design-tokens 0.2.0.
  lines.push('/* Density tier — overrides --bl-space-scale */');
  lines.push('[data-density="compact"]    { --bl-space-scale: 0.875; }');
  lines.push('[data-density="comfortable"] { --bl-space-scale: 1; }');
  lines.push('[data-density="spacious"]   { --bl-space-scale: 1.125; }');
  lines.push('');

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

// ── 2. TypeScript ────────────────────────────────────────────────────
function generateTS(): string {
  return [
    '// Auto-generated from bytelyst.tokens.json — do not edit manually',
    '',
    `export const tokens = ${JSON.stringify(tokens, null, 2)} as const;`,
    '',
    'export type Tokens = typeof tokens;',
    '',
  ].join('\n');
}

// ── 3. Kotlin ────────────────────────────────────────────────────────
function generateKotlin(): string {
  const lines: string[] = [
    '// Auto-generated from bytelyst.tokens.json — do not edit manually',
    'package com.mindlyst.shared.theme',
    '',
    '/**',
    ' * Cross-platform design tokens from bytelyst.tokens.json.',
    ' * Single source of truth consumed by both Android (Compose) and iOS (SwiftUI).',
    ' */',
    'object MindLystTokens {',
    '',
  ];

  // ── Palette
  lines.push('    // ── Color Palette ────────────────────────────────────────────────');
  lines.push('    object Palette {');
  for (const [key, value] of Object.entries(tokens.color.palette.neutral)) {
    lines.push(
      `        const val NEUTRAL_${key} = 0xFF${(value as string).replace('#', '').toUpperCase()}`
    );
  }
  lines.push('');
  for (const [key, value] of Object.entries(tokens.color.palette.brand)) {
    lines.push(
      `        const val ${key.toUpperCase()} = 0xFF${(value as string).replace('#', '').toUpperCase()}`
    );
  }
  lines.push('    }', '');

  // ── Dark semantic
  lines.push('    // ── Semantic Colors (Dark Theme) ─────────────────────────────────');
  lines.push('    object Dark {');
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(
        `        const val ${camelToScreamingSnake(key)} = 0xFF${value.replace('#', '').toUpperCase()}`
      );
    }
  }
  lines.push('    }', '');

  // ── Light semantic
  lines.push('    // ── Semantic Colors (Light Theme) ────────────────────────────────');
  lines.push('    object Light {');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(
        `        const val ${camelToScreamingSnake(key)} = 0xFF${value.replace('#', '').toUpperCase()}`
      );
    }
  }
  lines.push('    }', '');

  // ── Brain gradients
  lines.push('    // ── Brain Identity Gradients ─────────────────────────────────────');
  lines.push('    data class BrainGradient(val from: Long, val to: Long)');
  lines.push('');
  for (const [name, grad] of Object.entries(tokens.color.brain) as [
    string,
    { from: string; to: string },
  ][]) {
    lines.push(
      `    val BRAIN_${name.toUpperCase()} = BrainGradient(from = 0xFF${grad.from.replace('#', '').toUpperCase()}, to = 0xFF${grad.to.replace('#', '').toUpperCase()})`
    );
  }
  lines.push('');

  // ── Spacing
  lines.push('    // ── Spacing (8pt grid) ───────────────────────────────────────────');
  lines.push('    object Spacing {');
  for (const [key, value] of Object.entries(tokens.spacing)) {
    lines.push(`        const val X${key} = ${value}`);
  }
  lines.push('    }', '');

  // ── Radius
  lines.push('    // ── Radius ───────────────────────────────────────────────────────');
  lines.push('    object Radius {');
  for (const [key, value] of Object.entries(tokens.radius)) {
    lines.push(`        const val ${key.toUpperCase()} = ${value}`);
  }
  lines.push('    }', '');

  // ── Typography
  lines.push('    // ── Typography ───────────────────────────────────────────────────');
  lines.push('    object Typography {');
  for (const [key, value] of Object.entries(tokens.typography.fontFamily)) {
    // Extract just the primary font name (first in the list)
    const fontName =
      typeof value === 'string' ? value.split(',')[0].replace(/'/g, '').trim() : value;
    lines.push(`        const val FONT_${key.toUpperCase()} = "${fontName}"`);
  }
  lines.push('');
  for (const [key, value] of Object.entries(tokens.typography.fontSize)) {
    const sizeKey = key.toUpperCase().replace('-', '');
    lines.push(`        const val SIZE_${sizeKey} = ${value}`);
  }
  lines.push('    }', '');

  // ── Motion
  lines.push('    // ── Motion ───────────────────────────────────────────────────────');
  lines.push('    object Motion {');
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    lines.push(`        const val ${key.toUpperCase()} = ${value}`);
  }
  lines.push('    }', '');

  // ── Layout
  lines.push('    // ── Layout ───────────────────────────────────────────────────────');
  lines.push('    object Layout {');
  lines.push(`        const val TOUCH_TARGET_MIN = ${tokens.layout.touchTargetMin}`);
  lines.push(`        const val MOBILE_GUTTER    = ${tokens.layout.mobileGutter}`);
  lines.push(`        const val MAX_WIDTH        = ${tokens.layout.maxContentWidth}`);
  lines.push('    }');

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

// ── 4. Swift ─────────────────────────────────────────────────────────
function generateSwift(): string {
  const lines: string[] = [
    '// Auto-generated from bytelyst.tokens.json — do not edit manually',
    'import SwiftUI',
    '',
    '// MARK: - MindLyst Design Tokens (from shared KMP MindLystTokens)',
    '// These values mirror MindLystTokens.kt exactly.',
    '',
    'struct MindLystColors {',
  ];

  // Dark colors
  lines.push('    // Dark');
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(`    static let dark${capitalize(key)} = Color(hex: ${hexToUInt(value)})`);
    } else if (typeof value === 'string' && value.startsWith('rgba')) {
      // border/overlay → special handling
      if (key === 'borderDefault') {
        lines.push('    static let darkBorder  = Color.white.opacity(0.12)');
      }
    }
  }
  lines.push('');

  // Light colors
  lines.push('    // Light');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      if (value === '#FFFFFF') {
        lines.push(`    static let light${capitalize(key)} = Color.white`);
      } else {
        lines.push(`    static let light${capitalize(key)} = Color(hex: ${hexToUInt(value)})`);
      }
    }
  }
  lines.push('');

  // Brain gradients
  lines.push('    // Brain Gradients');
  for (const [name, grad] of Object.entries(tokens.color.brain) as [
    string,
    { from: string; to: string },
  ][]) {
    lines.push(
      `    static let brain${capitalize(name)} = Gradient(colors: [Color(hex: ${hexToUInt(grad.from)}), Color(hex: ${hexToUInt(grad.to)})])`
    );
  }
  lines.push('}', '');

  // Spacing
  lines.push('struct MindLystSpacing {');
  for (const [key, value] of Object.entries(tokens.spacing)) {
    const pad = key.length === 1 ? ' ' : '';
    lines.push(`    static let x${key}: ${pad}CGFloat = ${value}`);
  }
  lines.push('}', '');

  // Radius
  lines.push('struct MindLystRadius {');
  for (const [key, value] of Object.entries(tokens.radius)) {
    const pad = key.length < 4 ? ' '.repeat(4 - key.length) : '';
    lines.push(`    static let ${key}:${pad} CGFloat = ${value}`);
  }
  lines.push('}', '');

  // Motion (durations in seconds)
  lines.push('struct MindLystMotion {');
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    const seconds = (value as number) / 1000;
    const pad = key.length < 7 ? ' '.repeat(7 - key.length) : '';
    lines.push(`    static let ${key}:${pad} Double = ${seconds.toFixed(2)}`);
  }
  lines.push('}', '');

  // Color hex extension
  lines.push('// MARK: - Color Hex Extension');
  lines.push('');
  lines.push('extension Color {');
  lines.push('    init(hex: UInt, alpha: Double = 1.0) {');
  lines.push('        self.init(');
  lines.push('            .sRGB,');
  lines.push('            red:   Double((hex >> 16) & 0xFF) / 255.0,');
  lines.push('            green: Double((hex >> 8)  & 0xFF) / 255.0,');
  lines.push('            blue:  Double(hex & 0xFF) / 255.0,');
  lines.push('            opacity: alpha');
  lines.push('        )');
  lines.push('    }');
  lines.push('}', '');

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

// ── 5. Per-product CSS ──────────────────────────────────────────────
function generateProductCSS(productId: string, prefix: string, colorsKey: string): string {
  const productColors = tokens.color[colorsKey];
  if (!productColors) return `/* No palette found for ${productId} (color.${colorsKey}) */\n`;

  const lines: string[] = [
    `/* Auto-generated ${productId} tokens from bytelyst.tokens.json — do not edit manually */`,
    '',
    ':root {',
  ];

  // Semantic colors (dark as default) — shared across all products
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    lines.push(`  --${prefix}-${camelToKebab(key)}: ${value};`);
  }
  lines.push('');

  // Product-specific colors
  lines.push(`  /* ${productId} product colors */`);
  for (const [key, value] of Object.entries(productColors)) {
    if (typeof value === 'string') {
      lines.push(`  --${prefix}-${camelToKebab(key)}: ${value};`);
    } else if (typeof value === 'object' && value !== null && 'from' in value && 'to' in value) {
      const grad = value as unknown as { from: string; to: string };
      lines.push(`  --${prefix}-${camelToKebab(key)}-from: ${grad.from};`);
      lines.push(`  --${prefix}-${camelToKebab(key)}-to: ${grad.to};`);
    }
  }
  lines.push('');

  // Typography
  for (const [key, value] of Object.entries(tokens.typography.fontFamily)) {
    const cssVal = typeof value === 'string' ? value.replace(/'/g, '"') : value;
    lines.push(`  --${prefix}-font-${key}: ${cssVal};`);
  }
  lines.push('');

  // Font sizes
  for (const [key, value] of Object.entries(tokens.typography.fontSize)) {
    lines.push(`  --${prefix}-fs-${key}: ${value}px;`);
  }
  lines.push('');

  // Spacing
  for (const [key, value] of Object.entries(tokens.spacing)) {
    lines.push(`  --${prefix}-space-${key}: ${value === 0 ? '0' : `${value}px`};`);
  }
  lines.push('');

  // Radius
  for (const [key, value] of Object.entries(tokens.radius)) {
    lines.push(`  --${prefix}-radius-${key}: ${value}px;`);
  }
  lines.push('');

  // Elevation
  for (const [key, value] of Object.entries(tokens.elevation)) {
    if (key === 'none') continue;
    lines.push(`  --${prefix}-elevation-${key}: ${value};`);
  }
  lines.push('');

  // Motion
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    if (key === 'instant') continue;
    lines.push(`  --${prefix}-motion-${key}: ${value}ms;`);
  }
  lines.push(`  --${prefix}-easing-standard: ${tokens.motion.easing.standard};`);
  lines.push('');

  // @bytelyst/ui alias layer — maps --bl-* → product --{prefix}-* so shared
  // components resolve the correct product-scoped tokens.
  lines.push(`  /* ── @bytelyst/ui alias layer (--bl-* → --${prefix}-*) ──────────────── */`);
  lines.push(`  --bl-bg-canvas: var(--${prefix}-bg-canvas);`);
  lines.push(`  --bl-bg-elevated: var(--${prefix}-bg-elevated);`);
  lines.push(`  --bl-surface-card: var(--${prefix}-surface-card);`);
  lines.push(`  --bl-surface-muted: var(--${prefix}-surface-muted);`);
  lines.push(
    `  --bl-surface-highlight: color-mix(in oklab, var(--${prefix}-surface-muted) 82%, white);`
  );
  lines.push(
    `  --bl-surface-overlay: color-mix(in oklab, var(--${prefix}-bg-canvas) 88%, transparent);`
  );
  lines.push(
    `  --bl-input: color-mix(in oklab, var(--${prefix}-surface-muted) 76%, var(--${prefix}-bg-canvas));`
  );
  lines.push(`  --bl-border: var(--${prefix}-border-default);`);
  lines.push(`  --bl-border-strong: var(--${prefix}-border-strong);`);
  lines.push(
    `  --bl-border-subtle: color-mix(in oklab, var(--${prefix}-border-default) 62%, transparent);`
  );
  lines.push(`  --bl-text-primary: var(--${prefix}-text-primary);`);
  lines.push(`  --bl-text-secondary: var(--${prefix}-text-secondary);`);
  lines.push(`  --bl-text-tertiary: var(--${prefix}-text-tertiary);`);
  lines.push(
    `  --bl-text-quiet: color-mix(in oklab, var(--${prefix}-text-secondary) 78%, var(--${prefix}-bg-canvas));`
  );
  lines.push(`  --bl-accent: var(--${prefix}-accent-primary);`);
  lines.push(`  --bl-accent-foreground: var(--${prefix}-bg-canvas);`);
  lines.push(
    `  --bl-accent-muted: color-mix(in oklab, var(--${prefix}-accent-primary) 16%, transparent);`
  );
  lines.push(`  --bl-info: var(--${prefix}-accent-primary);`);
  lines.push(
    `  --bl-info-muted: color-mix(in oklab, var(--${prefix}-accent-primary) 14%, transparent);`
  );
  lines.push(`  --bl-success: var(--${prefix}-success);`);
  lines.push(
    `  --bl-success-muted: color-mix(in oklab, var(--${prefix}-success) 14%, transparent);`
  );
  lines.push(`  --bl-warning: var(--${prefix}-warning);`);
  lines.push(
    `  --bl-warning-muted: color-mix(in oklab, var(--${prefix}-warning) 14%, transparent);`
  );
  lines.push(`  --bl-danger: var(--${prefix}-danger);`);
  lines.push(`  --bl-danger-muted: color-mix(in oklab, var(--${prefix}-danger) 14%, transparent);`);
  lines.push(`  --bl-danger-foreground: var(--${prefix}-bg-canvas);`);
  lines.push(`  --bl-focus-ring: var(--${prefix}-focus-ring);`);
  lines.push(
    `  --bl-focus-ring-muted: color-mix(in oklab, var(--${prefix}-accent-primary) 18%, transparent);`
  );
  lines.push(`  --bl-overlay-scrim: var(--${prefix}-overlay-scrim);`);

  lines.push('}', '');

  // Light theme overrides
  lines.push('[data-theme="light"] {');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    const darkVal = (tokens.color.semantic.dark as Record<string, unknown>)[key];
    if (value !== darkVal) {
      lines.push(`  --${prefix}-${camelToKebab(key)}: ${value};`);
    }
  }
  lines.push('}', '');

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

// ── Product native mapping (products with iOS/Android apps) ─────────
interface ProductNativeConfig {
  colorsKey: string;
  swiftEnum: string; // e.g. 'PeakPulseColors'
  kotlinObject: string; // e.g. 'PeakPulseTokens'
  kotlinPackage: string; // e.g. 'com.peakpulse.theme'
  swiftFile: string; // e.g. 'PeakPulseTheme.swift'
  kotlinFile: string; // e.g. 'PeakPulseTokens.kt'
}

const PRODUCT_NATIVE_MAP: Record<string, ProductNativeConfig> = {
  chronomind: {
    colorsKey: 'chronomind',
    swiftEnum: 'CMColors',
    kotlinObject: 'ChronoMindTokens',
    kotlinPackage: 'com.chronomind.app.theme',
    swiftFile: 'ChronoMindTheme.generated.swift',
    kotlinFile: 'ChronoMindTokens.generated.kt',
  },
  jarvisjr: {
    colorsKey: 'jarvisjr',
    swiftEnum: 'JarvisJrColors',
    kotlinObject: 'JarvisJrTokens',
    kotlinPackage: 'com.jarvisjr.app.theme',
    swiftFile: 'JarvisJrTheme.generated.swift',
    kotlinFile: 'JarvisJrTokens.generated.kt',
  },
  peakpulse: {
    colorsKey: 'peakpulse',
    swiftEnum: 'PeakPulseColors',
    kotlinObject: 'PeakPulseTokens',
    kotlinPackage: 'com.peakpulse.theme',
    swiftFile: 'PeakPulseTheme.generated.swift',
    kotlinFile: 'PeakPulseTokens.generated.kt',
  },
  lysnrai: {
    colorsKey: 'lysnrai',
    swiftEnum: 'LysnrAIColors',
    kotlinObject: 'LysnrAITokens',
    kotlinPackage: 'com.saravana.lysnrai.theme',
    swiftFile: 'LysnrAITheme.generated.swift',
    kotlinFile: 'LysnrAITokens.generated.kt',
  },
  nomgap: {
    colorsKey: 'nomgap',
    swiftEnum: 'NomGapColors',
    kotlinObject: 'NomGapTokens',
    kotlinPackage: 'com.nomgap.theme',
    swiftFile: 'NomGapTheme.generated.swift',
    kotlinFile: 'NomGapTokens.generated.kt',
  },
  actiontrail: {
    colorsKey: 'actiontrail',
    swiftEnum: 'ActionTrailColors',
    kotlinObject: 'ActionTrailTokens',
    kotlinPackage: 'com.actiontrail.theme',
    swiftFile: 'ActionTrailTheme.generated.swift',
    kotlinFile: 'ActionTrailTokens.generated.kt',
  },
  flowmonk: {
    colorsKey: 'flowmonk',
    swiftEnum: 'FlowMonkColors',
    kotlinObject: 'FlowMonkTokens',
    kotlinPackage: 'com.flowmonk.theme',
    swiftFile: 'FlowMonkTheme.generated.swift',
    kotlinFile: 'FlowMonkTokens.generated.kt',
  },
  notelett: {
    colorsKey: 'notelett',
    swiftEnum: 'NoteLettColors',
    kotlinObject: 'NoteLettTokens',
    kotlinPackage: 'com.notelett.theme',
    swiftFile: 'NoteLettTheme.generated.swift',
    kotlinFile: 'NoteLettTokens.generated.kt',
  },
  localmemgpt: {
    colorsKey: 'localmemgpt',
    swiftEnum: 'LocalMemGPTColors',
    kotlinObject: 'LocalMemGPTTokens',
    kotlinPackage: 'com.localmemgpt.theme',
    swiftFile: 'LocalMemGPTTheme.generated.swift',
    kotlinFile: 'LocalMemGPTTokens.generated.kt',
  },
  localllmlab: {
    colorsKey: 'localllmlab',
    swiftEnum: 'LocalLLMLabColors',
    kotlinObject: 'LocalLLMLabTokens',
    kotlinPackage: 'com.localllmlab.theme',
    swiftFile: 'LocalLLMLabTheme.generated.swift',
    kotlinFile: 'LocalLLMLabTokens.generated.kt',
  },
};

// ── 6. Per-product Swift ─────────────────────────────────────────────
function generateProductSwift(productId: string, config: ProductNativeConfig): string {
  const productColors = tokens.color[config.colorsKey];
  const lines: string[] = [
    `// Auto-generated from bytelyst.tokens.json — do not edit manually.`,
    `// Product: ${productId}`,
    `// Regenerate: cd packages/design-tokens && tsx scripts/generate.ts`,
    '',
    'import SwiftUI',
    '',
    `enum ${config.swiftEnum} {`,
  ];

  // Semantic dark colors
  lines.push('    // MARK: - Semantic (Dark Theme)');
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(`    static let ${key} = Color(hex: ${hexToUInt(value)})`);
    }
  }
  lines.push('');

  // Product-specific colors
  if (productColors) {
    lines.push(`    // MARK: - ${capitalize(productId)} Product Colors`);
    for (const [key, value] of Object.entries(productColors)) {
      if (typeof value === 'string' && value.startsWith('#')) {
        lines.push(`    static let ${key} = Color(hex: ${hexToUInt(value)})`);
      } else if (typeof value === 'object' && value !== null && 'from' in value) {
        const grad = value as { from: string; to: string };
        lines.push(
          `    static let ${key} = Gradient(colors: [Color(hex: ${hexToUInt(grad.from)}), Color(hex: ${hexToUInt(grad.to)})])`
        );
      }
    }
    lines.push('');
  }

  lines.push('}', '');

  // Semantic light colors
  lines.push(`enum ${config.swiftEnum}Light {`);
  lines.push('    // MARK: - Semantic (Light Theme)');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(`    static let ${key} = Color(hex: ${hexToUInt(value)})`);
    }
  }
  lines.push('}', '');

  // Spacing
  lines.push(`enum ${config.swiftEnum.replace('Colors', '')}Spacing {`);
  for (const [key, value] of Object.entries(tokens.spacing)) {
    const pad = key.length === 1 ? ' ' : '';
    lines.push(`    static let x${key}: ${pad}CGFloat = ${value}`);
  }
  lines.push('}', '');

  // Radius
  lines.push(`enum ${config.swiftEnum.replace('Colors', '')}Radius {`);
  for (const [key, value] of Object.entries(tokens.radius)) {
    lines.push(`    static let ${key}: CGFloat = ${value}`);
  }
  lines.push('}', '');

  // Motion
  lines.push(`enum ${config.swiftEnum.replace('Colors', '')}Motion {`);
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    const seconds = (value as number) / 1000;
    lines.push(`    static let ${key}: Double = ${seconds.toFixed(2)}`);
  }
  lines.push('}', '');

  // Color hex extension (only include if not already in project)
  lines.push('// MARK: - Color Hex Extension (import if not already defined)');
  lines.push('');
  lines.push('extension Color {');
  lines.push('    init(hex: UInt, alpha: Double = 1.0) {');
  lines.push('        self.init(');
  lines.push('            .sRGB,');
  lines.push('            red:   Double((hex >> 16) & 0xFF) / 255.0,');
  lines.push('            green: Double((hex >> 8)  & 0xFF) / 255.0,');
  lines.push('            blue:  Double(hex & 0xFF) / 255.0,');
  lines.push('            opacity: alpha');
  lines.push('        )');
  lines.push('    }');
  lines.push('}', '');

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

// ── 7. Per-product Kotlin ────────────────────────────────────────────
function generateProductKotlin(productId: string, config: ProductNativeConfig): string {
  const productColors = tokens.color[config.colorsKey];
  const lines: string[] = [
    '// Auto-generated from bytelyst.tokens.json — do not edit manually.',
    `// Product: ${productId}`,
    '// Regenerate: cd packages/design-tokens && tsx scripts/generate.ts',
    `package ${config.kotlinPackage}`,
    '',
    `object ${config.kotlinObject} {`,
    '',
  ];

  // Semantic dark
  lines.push('    // ── Semantic Colors (Dark Theme) ─────────────────────────────────');
  lines.push('    object Dark {');
  for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(
        `        const val ${camelToScreamingSnake(key)} = 0xFF${value.replace('#', '').toUpperCase()}`
      );
    }
  }
  lines.push('    }', '');

  // Semantic light
  lines.push('    // ── Semantic Colors (Light Theme) ────────────────────────────────');
  lines.push('    object Light {');
  for (const [key, value] of Object.entries(tokens.color.semantic.light)) {
    if (typeof value === 'string' && value.startsWith('#')) {
      lines.push(
        `        const val ${camelToScreamingSnake(key)} = 0xFF${value.replace('#', '').toUpperCase()}`
      );
    }
  }
  lines.push('    }', '');

  // Product-specific colors
  if (productColors) {
    lines.push(`    // ── ${capitalize(productId)} Product Colors ───────────────────────────────`);
    lines.push('    object Product {');
    for (const [key, value] of Object.entries(productColors)) {
      if (typeof value === 'string' && value.startsWith('#')) {
        lines.push(
          `        const val ${camelToScreamingSnake(key)} = 0xFF${value.replace('#', '').toUpperCase()}`
        );
      } else if (typeof value === 'object' && value !== null && 'from' in value) {
        const grad = value as { from: string; to: string };
        lines.push(
          `        const val ${camelToScreamingSnake(key)}_FROM = 0xFF${grad.from.replace('#', '').toUpperCase()}`
        );
        lines.push(
          `        const val ${camelToScreamingSnake(key)}_TO = 0xFF${grad.to.replace('#', '').toUpperCase()}`
        );
      }
    }
    lines.push('    }', '');
  }

  // Spacing
  lines.push('    // ── Spacing (8pt grid) ───────────────────────────────────────────');
  lines.push('    object Spacing {');
  for (const [key, value] of Object.entries(tokens.spacing)) {
    lines.push(`        const val X${key} = ${value}`);
  }
  lines.push('    }', '');

  // Radius
  lines.push('    // ── Radius ───────────────────────────────────────────────────────');
  lines.push('    object Radius {');
  for (const [key, value] of Object.entries(tokens.radius)) {
    lines.push(`        const val ${key.toUpperCase()} = ${value}`);
  }
  lines.push('    }', '');

  // Typography
  lines.push('    // ── Typography ───────────────────────────────────────────────────');
  lines.push('    object Typography {');
  for (const [key, value] of Object.entries(tokens.typography.fontFamily)) {
    const fontName =
      typeof value === 'string' ? value.split(',')[0].replace(/'/g, '').trim() : value;
    lines.push(`        const val FONT_${key.toUpperCase()} = "${fontName}"`);
  }
  lines.push('    }', '');

  // Motion
  lines.push('    // ── Motion ───────────────────────────────────────────────────────');
  lines.push('    object Motion {');
  for (const [key, value] of Object.entries(tokens.motion.duration)) {
    lines.push(`        const val ${key.toUpperCase()} = ${value}`);
  }
  lines.push('    }');

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

// ── Write all ────────────────────────────────────────────────────────
// Shared semantic tokens (backward compatible)
writeFileSync(resolve(outDir, 'tokens.css'), generateCSS());
writeFileSync(resolve(outDir, 'tokens.ts'), generateTS());
writeFileSync(resolve(outDir, 'MindLystTokens.kt'), generateKotlin());
writeFileSync(resolve(outDir, 'MindLystTheme.swift'), generateSwift());

// Per-product CSS
for (const [productId, config] of Object.entries(PRODUCT_CSS_MAP)) {
  const css = generateProductCSS(productId, config.prefix, config.colorsKey);
  writeFileSync(resolve(outDir, `${productId}.css`), css);
}

// Per-product Swift + Kotlin
const nativeDir = resolve(outDir, 'native');
mkdirSync(nativeDir, { recursive: true });
for (const [productId, config] of Object.entries(PRODUCT_NATIVE_MAP)) {
  const swift = generateProductSwift(productId, config);
  writeFileSync(resolve(nativeDir, config.swiftFile), swift);
  const kotlin = generateProductKotlin(productId, config);
  writeFileSync(resolve(nativeDir, config.kotlinFile), kotlin);
}

console.log(
  `Generated 4 shared + ${Object.keys(PRODUCT_CSS_MAP).length} product CSS + ${Object.keys(PRODUCT_NATIVE_MAP).length * 2} native token files in generated/`
);
