// TypeScript token-types emitter.
//
// Writes `<host>-tokens.d.ts` — strict literal-union types for every
// extracted token so component props like `color?: ColorToken` get
// autocompleted and type-checked against the actual brand, not a
// generic `string`. Pairs with the `<host>-theme.js` JS theme object.

function safeKey(s) {
  return String(s || '')
    .replace(/[^a-zA-Z0-9_$]/g, '_')
    .replace(/^([0-9])/, '_$1')
    .toLowerCase();
}

function asScalar(v) {
  if (v == null) return null;
  if (typeof v === 'string' || typeof v === 'number') return String(v);
  if (typeof v === 'object') {
    if (typeof v.value  !== 'undefined') return asScalar(v.value);
    if (typeof v.px     !== 'undefined') return String(v.px);
    if (typeof v.weight !== 'undefined') return String(v.weight);
    if (typeof v.ms     !== 'undefined') return String(v.ms);
    if (typeof v.hex    !== 'undefined') return String(v.hex);
    if (typeof v.name   !== 'undefined') return String(v.name);
    return null; // unknown shape — drop
  }
  return null;
}

function quoteUnion(values) {
  const unique = [...new Set(values.map(asScalar).filter(Boolean))];
  if (unique.length === 0) return 'never';
  return unique.map((v) => `'${v.replace(/'/g, "\\'")}'`).join(' | ');
}

export function formatTsDefs(design) {
  const lines = [
    '// Auto-generated by designlang. Do not edit by hand.',
    '// Re-extract with `npx designlang <url>` to refresh.',
    '//',
    '// Import next to your token JSON to get type-safe access:',
    '//',
    '//   import type { ColorToken, SpacingToken } from \'./<host>-tokens\';',
    '//   import tokens from \'./<host>-design-tokens.json\';',
    '//',
    `// Source: ${design.meta?.url || '—'}`,
    `// Generated: ${new Date().toISOString()}`,
    '',
    'export type Hex = `#${string}`;',
    'export type Px  = `${number}px`;',
    'export type Ms  = `${number}ms`;',
    '',
  ];

  // colour role union (primary | secondary | ...)
  const colourRoles = ['primary', 'secondary', 'accent']
    .filter((r) => design.colors?.[r]?.hex);
  lines.push(`export type ColorRole = ${quoteUnion(colourRoles) || quoteUnion(['primary'])};`);

  // every unique hex
  const palette = (design.colors?.all || []).map((c) => c.hex).filter(Boolean);
  lines.push(`export type ColorHex  = ${quoteUnion(palette) || 'Hex'};`);
  lines.push('export type ColorToken = ColorRole | ColorHex;');
  lines.push('');

  // typography
  const fonts = (design.typography?.families || []).map((f) => f.name || f).filter(Boolean);
  lines.push(`export type FontFamilyToken = ${quoteUnion(fonts) || quoteUnion(['system'])};`);

  const sizes = (design.typography?.sizes || []).map((s) => `${typeof s === 'object' ? s.value || s.px : s}px`).filter(Boolean);
  lines.push(`export type FontSizeToken   = ${quoteUnion(sizes) || 'Px'};`);

  const weights = (design.typography?.weights || []);
  lines.push(`export type FontWeightToken = ${quoteUnion(weights) || `'400' | '500' | '600' | '700'`};`);
  lines.push('');

  // spacing
  const spacing = (design.spacing?.scale || []).map((px) => `${px}px`).filter(Boolean);
  lines.push(`export type SpacingToken = ${quoteUnion(spacing) || 'Px'};`);
  lines.push('');

  // radii
  const radii = (design.borders?.radii || []).map((r) => `${typeof r === 'object' ? r.value : r}px`).filter(Boolean);
  lines.push(`export type RadiusToken = ${quoteUnion(radii) || 'Px'};`);
  lines.push('');

  // motion
  const durations = (design.motion?.durations || []).map((d) => `${typeof d === 'object' ? (d.value || d.ms) : d}ms`).filter(Boolean);
  lines.push(`export type DurationToken = ${quoteUnion(durations) || 'Ms'};`);

  const easings = (design.motion?.easings || []).map((e) => typeof e === 'object' ? e.value : e).filter(Boolean);
  lines.push(`export type EasingToken   = ${quoteUnion(easings) || 'string'};`);
  lines.push('');

  // semantic intent
  if (design.pageIntent?.label) {
    lines.push(`export type PageIntent = ${quoteUnion([design.pageIntent.label])};`);
  }
  if (design.materialLanguage?.label) {
    lines.push(`export type MaterialLanguage = ${quoteUnion([design.materialLanguage.label])};`);
  }

  // a roll-up interface for the whole token tree
  lines.push('');
  lines.push('export interface DesignTokens {');
  lines.push('  color: { [role in ColorRole]: ColorHex };');
  lines.push('  typography: {');
  lines.push('    family: FontFamilyToken;');
  lines.push('    size: FontSizeToken;');
  lines.push('    weight: FontWeightToken;');
  lines.push('  };');
  lines.push('  spacing: ReadonlyArray<SpacingToken>;');
  lines.push('  radius:  ReadonlyArray<RadiusToken>;');
  lines.push('  motion: {');
  lines.push('    duration: ReadonlyArray<DurationToken>;');
  lines.push('    easing:   ReadonlyArray<EasingToken>;');
  lines.push('  };');
  lines.push('}');
  lines.push('');
  lines.push('declare const tokens: DesignTokens;');
  lines.push('export default tokens;');
  lines.push('');

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