import { promises as fs } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const STYLES_DIR = path.resolve(__dirname, '../src/styles')
const THEME_OUTPUT_PATH = path.join(STYLES_DIR, 'theme.css')

const RUNTIME_THEME_INPUT_LINES = [
  '--cs-theme-primary: var(--cs-primary);',
  '--cs-theme-ring: color-mix(in srgb, var(--cs-theme-primary) 40%, transparent);'
]

const COMPATIBILITY_ALIAS_LINES = ['--primary: var(--color-primary);', '--ring: var(--color-ring);']

const PRIMARY_SEMANTIC_LINES = [
  '--color-primary: var(--cs-theme-primary);',
  '--color-primary-hover: var(--cs-primary-hover);',
  '--color-primary-soft: color-mix(in srgb, var(--color-primary) 60%, transparent);',
  '--color-primary-mute: color-mix(in srgb, var(--color-primary) 30%, transparent);',
  '--color-ring: var(--cs-theme-ring);'
]

const SPACING_COMMENT_LINES = [
  '/* Keep spacing opt-in for now to avoid overriding Tailwind container names. */',
  '/* --spacing-5xs: var(--cs-size-5xs);',
  '--spacing-4xs: var(--cs-size-4xs);',
  '--spacing-3xs: var(--cs-size-3xs);',
  '--spacing-2xs: var(--cs-size-2xs);',
  '--spacing-xs: var(--cs-size-xs);',
  '--spacing-sm: var(--cs-size-sm);',
  '--spacing-md: var(--cs-size-md);',
  '--spacing-lg: var(--cs-size-lg);',
  '--spacing-xl: var(--cs-size-xl);',
  '--spacing-2xl: var(--cs-size-2xl);',
  '--spacing-3xl: var(--cs-size-3xl);',
  '--spacing-4xl: var(--cs-size-4xl);',
  '--spacing-5xl: var(--cs-size-5xl);',
  '--spacing-6xl: var(--cs-size-6xl);',
  '--spacing-7xl: var(--cs-size-7xl);',
  '--spacing-8xl: var(--cs-size-8xl); */'
]

const ANIMATION_LINES = [
  '--animate-checkbox-bounce: checkbox-bounce 300ms cubic-bezier(0.4, 0, 0.2, 1);',
  '--animate-checkbox-icon-in: checkbox-icon-in 160ms ease-out both;',
  '',
  '@keyframes checkbox-bounce {',
  '  0%,',
  '  100% {',
  '    transform: scale(1);',
  '  }',
  '',
  '  50% {',
  '    transform: scale(1.08);',
  '  }',
  '}',
  '',
  '@keyframes checkbox-icon-in {',
  '  from {',
  '    opacity: 0;',
  '    transform: scale(0.75);',
  '  }',
  '',
  '  to {',
  '    opacity: 1;',
  '    transform: scale(1);',
  '  }',
  '}'
]

export interface ThemeContractInputs {
  primitiveColors: string[]
  semanticColors: string[]
  statusColors: string[]
  radiusTokens: string[]
  typographyTokens: string[]
}

function dedupe(values: string[]): string[] {
  return [...new Set(values)]
}

export function extractTokenNames(source: string): string[] {
  return dedupe([...source.matchAll(/^\s*--cs-([a-z0-9-]+)\s*:/gm)].map((match) => match[1]))
}

function toPrefixedMappings(tokenNames: string[], targetPrefix: string, sourcePrefix = '--cs-'): string[] {
  return tokenNames.map((tokenName) => `--${targetPrefix}${tokenName}: var(${sourcePrefix}${tokenName});`)
}

function toDirectMappings(tokenNames: string[], sourcePrefix = '--cs-'): string[] {
  return tokenNames.map((tokenName) => `--${tokenName}: var(${sourcePrefix}${tokenName});`)
}

function buildSection(title: string, lines: string[]): string {
  const indentedLines = lines.map((line) => (line ? `  ${line}` : '')).join('\n')

  return `  /* ==================== */\n  /* ${title} */\n  /* ==================== */\n${indentedLines}`
}

export function buildThemeContractCss(inputs: ThemeContractInputs): string {
  const semanticContractTokens = inputs.semanticColors.filter(
    (token) => !['primary', 'primary-hover', 'ring'].includes(token)
  )

  const sections = [
    buildSection('Primitive Colors', toPrefixedMappings(inputs.primitiveColors, 'color-')),
    buildSection('Runtime Theme Inputs', RUNTIME_THEME_INPUT_LINES),
    buildSection('Compatibility Aliases', COMPATIBILITY_ALIAS_LINES),
    buildSection('Semantic Colors', [
      ...PRIMARY_SEMANTIC_LINES,
      ...toPrefixedMappings(semanticContractTokens, 'color-')
    ]),
    buildSection('Status Colors', toPrefixedMappings(inputs.statusColors, 'color-')),
    buildSection('Spacing', SPACING_COMMENT_LINES),
    buildSection('Radius', toDirectMappings(inputs.radiusTokens)),
    buildSection('Typography', toDirectMappings(inputs.typographyTokens)),
    buildSection('Animation', ANIMATION_LINES)
  ]

  return `/**
 * Generated from design tokens.
 *
 * ⚠️ DO NOT EDIT DIRECTLY!
 * This file is generated by \`pnpm theme:build\`.
 * Update \`src/styles/tokens/*\` to change the design source.
 */

@import './tokens.css';

@theme {
${sections.join('\n\n')}
}

@layer base {
  * {
    @apply border-border outline-ring/50;
  }

  body {
    @apply bg-background text-foreground;
  }
}
`
}

export async function loadThemeContractInputs(stylesDir = STYLES_DIR): Promise<ThemeContractInputs> {
  const tokensDir = path.join(stylesDir, 'tokens')
  const [primitiveColorsSource, semanticColorsSource, statusColorsSource, radiusSource, typographySource] =
    await Promise.all([
      fs.readFile(path.join(tokensDir, 'colors/primitive.css'), 'utf8'),
      fs.readFile(path.join(tokensDir, 'colors/semantic.css'), 'utf8'),
      fs.readFile(path.join(tokensDir, 'colors/status.css'), 'utf8'),
      fs.readFile(path.join(tokensDir, 'radius.css'), 'utf8'),
      fs.readFile(path.join(tokensDir, 'typography.css'), 'utf8')
    ])

  return {
    primitiveColors: extractTokenNames(primitiveColorsSource),
    semanticColors: extractTokenNames(semanticColorsSource),
    statusColors: extractTokenNames(statusColorsSource),
    radiusTokens: extractTokenNames(radiusSource),
    typographyTokens: extractTokenNames(typographySource)
  }
}

export async function writeThemeContractCss(outputPath = THEME_OUTPUT_PATH, stylesDir = STYLES_DIR): Promise<void> {
  const inputs = await loadThemeContractInputs(stylesDir)
  const css = buildThemeContractCss(inputs)
  await fs.writeFile(outputPath, css, 'utf8')
}

async function main() {
  await writeThemeContractCss()
}

if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
  void main()
}
