import { readFileSync } from 'fs';
import { join } from 'path';
import { APP_ROOT } from '../../../lib/runtime-paths.mjs';
import { PlatformError } from '../../kernel/errors.mjs';

const source = JSON.parse(readFileSync(join(APP_ROOT, 'public', 'design-system', 'tokens', 'source.json'), 'utf8'));
const BUILTIN_THEME_IDS = new Set([...source.themes, 'system', 'inherit']);
const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
const RGB_COLOR = /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(0|1|0?\.\d+))?\s*\)$/i;
const DIMENSION = /^-?\d+(?:\.\d+)?px$/i;
const THEME_ID = /^[a-z][a-z0-9-]{2,39}$/;

function expandHex(value) {
  const hex = value.replace('#', '');
  if (hex.length === 3 || hex.length === 4) return hex.split('').map(char => char + char).join('');
  return hex;
}

function parseColor(value) {
  const raw = String(value || '').trim();
  if (HEX_COLOR.test(raw)) {
    const hex = expandHex(raw);
    return [0, 2, 4].map(index => Number.parseInt(hex.slice(index, index + 2), 16));
  }
  const match = raw.match(RGB_COLOR);
  if (!match) return null;
  const channels = match.slice(1, 4).map(Number);
  return channels.every(channel => Number.isFinite(channel) && channel >= 0 && channel <= 255) ? channels : null;
}

function validColor(value) {
  if (!parseColor(value)) return false;
  const match = String(value).trim().match(RGB_COLOR);
  if (!match) return true;
  const alpha = match[4] === undefined ? 1 : Number(match[4]);
  return Number.isFinite(alpha) && alpha >= 0 && alpha <= 1;
}

function validateValue(token, value) {
  const raw = String(value ?? '').trim();
  if (token.type === 'color') return validColor(raw);
  if (token.type === 'dimension') {
    if (!DIMENSION.test(raw)) return false;
    const numeric = Number.parseFloat(raw);
    if (token.min !== undefined && numeric < Number(token.min)) return false;
    if (token.max !== undefined && numeric > Number(token.max)) return false;
    return true;
  }
  return false;
}

function relativeLuminance(value) {
  const rgb = parseColor(value);
  if (!rgb) return null;
  const linear = rgb.map(channel => {
    const normalized = channel / 255;
    return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
  });
  return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
}

export function contrastRatio(foreground, background) {
  const first = relativeLuminance(foreground);
  const second = relativeLuminance(background);
  if (first === null || second === null) return null;
  const lighter = Math.max(first, second);
  const darker = Math.min(first, second);
  return (lighter + 0.05) / (darker + 0.05);
}

export function getBaseThemeValues(themeId = 'light') {
  const base = source.themes.includes(themeId) ? themeId : 'light';
  return Object.fromEntries(Object.entries(source.tokens).map(([name, token]) => [name, token[base]]));
}

export function resolveThemeValues({ baseTheme = 'light', values = {} } = {}) {
  return Object.freeze({ ...getBaseThemeValues(baseTheme), ...values });
}

export function evaluateThemeAccessibility({ baseTheme = 'light', values = {} } = {}) {
  const resolved = resolveThemeValues({ baseTheme, values });
  const checks = [
    ['primary-on-canvas', 'color.ink', 'color.canvas', 4.5],
    ['primary-on-surface', 'color.ink', 'color.surface', 4.5],
    ['secondary-on-surface', 'color.inkSecondary', 'color.surface', 4.5],
    ['muted-on-surface', 'color.inkMuted', 'color.surface', 3],
    ['brand-on-surface', 'color.brand', 'color.surface', 3],
    ['danger-on-surface', 'color.danger', 'color.surface', 3],
    ['line-on-surface', 'color.lineStrong', 'color.surface', 3],
  ].map(([id, foregroundToken, backgroundToken, minimum]) => {
    const ratio = contrastRatio(resolved[foregroundToken], resolved[backgroundToken]);
    return {
      id,
      foregroundToken,
      backgroundToken,
      ratio: ratio === null ? null : Number(ratio.toFixed(2)),
      minimum,
      ok: ratio !== null && ratio >= minimum,
    };
  });
  return Object.freeze({
    ok: checks.every(check => check.ok),
    checks: Object.freeze(checks),
    failures: Object.freeze(checks.filter(check => !check.ok)),
  });
}

export function getThemeContract() {
  return {
    schemaVersion: source.schemaVersion,
    contractVersion: source.contractVersion,
    themes: [...source.themes],
    tokens: Object.fromEntries(Object.entries(source.tokens).map(([name, token]) => [name, {
      css: token.css,
      type: token.type,
      customizable: Boolean(token.customizable),
      label: token.label || name,
      group: token.group || name.split('.')[0],
      ...(token.min !== undefined ? { min: token.min } : {}),
      ...(token.max !== undefined ? { max: token.max } : {}),
    }])),
  };
}

export function getBuiltinThemes() {
  return source.themes.map(id => ({
    id,
    name: ({ light: 'Hell', dark: 'Dunkel', contrast: 'Hoher Kontrast' })[id] || id,
    base_theme: id,
    builtin: true,
    values: {},
    resolved_values: getBaseThemeValues(id),
    accessibility: evaluateThemeAccessibility({ baseTheme: id }),
  }));
}

export function validateThemeDefinition(theme, { allowExistingId = false } = {}) {
  if (!theme || typeof theme !== 'object' || Array.isArray(theme)) {
    throw new PlatformError('Theme-Definition ist ungültig.', { code: 'THEME_INVALID', status: 400, expose: true });
  }
  const id = String(theme.id || '').trim().toLowerCase();
  const name = String(theme.name || '').trim();
  const baseTheme = String(theme.base_theme || theme.baseTheme || 'light').trim().toLowerCase();
  if (!THEME_ID.test(id) || BUILTIN_THEME_IDS.has(id)) throw new PlatformError('Theme-ID ist ungültig oder reserviert.', { code: 'THEME_ID_INVALID', status: 400, expose: true });
  if (!allowExistingId && !id.startsWith('custom-')) throw new PlatformError('Eigene Theme-IDs müssen mit custom- beginnen.', { code: 'THEME_ID_PREFIX', status: 400, expose: true });
  if (!name || name.length > 80) throw new PlatformError('Theme-Name ist ungültig.', { code: 'THEME_NAME_INVALID', status: 400, expose: true });
  if (!source.themes.includes(baseTheme)) throw new PlatformError('Basis-Theme ist ungültig.', { code: 'THEME_BASE_INVALID', status: 400, expose: true });
  const values = {};
  for (const [tokenName, value] of Object.entries(theme.values || {})) {
    const token = source.tokens[tokenName];
    if (!token || !token.customizable) throw new PlatformError(`Token ist nicht anpassbar: ${tokenName}`, { code: 'THEME_TOKEN_FORBIDDEN', status: 400, expose: true });
    if (!validateValue(token, value)) throw new PlatformError(`Ungültiger Wert für ${tokenName}.`, { code: 'THEME_VALUE_INVALID', status: 400, expose: true });
    values[tokenName] = String(value).trim();
  }
  const accessibility = evaluateThemeAccessibility({ baseTheme, values });
  return Object.freeze({ schemaVersion: 1, id, name, base_theme: baseTheme, values: Object.freeze(values), accessibility });
}

export function validateThemePreference(input, { customThemeExists = () => false } = {}) {
  const themeId = String(input?.theme_id || input?.themeId || 'inherit').trim().toLowerCase();
  const density = String(input?.density || 'comfortable').trim().toLowerCase();
  const reducedMotion = String(input?.reduced_motion || input?.reducedMotion || 'system').trim().toLowerCase();
  if (![...BUILTIN_THEME_IDS].includes(themeId) && !customThemeExists(themeId)) {
    throw new PlatformError('Gewähltes Theme existiert nicht.', { code: 'THEME_NOT_FOUND', status: 404, expose: true });
  }
  if (!['compact', 'comfortable', 'spacious'].includes(density)) throw new PlatformError('UI-Dichte ist ungültig.', { code: 'THEME_DENSITY_INVALID', status: 400, expose: true });
  if (!['system', 'reduce', 'allow'].includes(reducedMotion)) throw new PlatformError('Bewegungseinstellung ist ungültig.', { code: 'THEME_MOTION_INVALID', status: 400, expose: true });
  return Object.freeze({ theme_id: themeId, density, reduced_motion: reducedMotion });
}
