#!/usr/bin/env node
/**
 * Vergleicht alle frontend/src/i18n/locales/*.json mit de.json (wie deepMerge in frontend/src/i18n/index.js).
 *
 * Metriken pro Locale (Blatt-Strings, flatten):
 * - inheritedFromDe: Key fehlt in der Locale-Datei → Merge ergibt deutschen Text
 * - explicitStillDe: Key ist in der Locale gesetzt, Wert ist identisch zu de (Copy-Paste / vergessen)
 * - differsFromDe: abweichend von Deutsch (Zielsprache oder Englisch-Vorlage)
 * - enUsLeak: unter differsFromDe: Wert identisch zum en-US-Merge (typisch EN-Vorlage statt Zielsprache)
 *
 * Usage:
 *   node scripts/check-i18n-completeness.js
 *   node scripts/check-i18n-completeness.js --top 25 --locale fil
 */

const fs = require('fs');
const path = require('path');

const ROOT = path.resolve(__dirname, '..');
const SOURCE_DIR = path.join(ROOT, 'frontend', 'src', 'i18n', 'locales');

function deepMergeMessages(base, override) {
  if (!base || typeof base !== 'object' || Array.isArray(base)) {
    return override ?? base;
  }
  const result = { ...base };
  for (const [key, value] of Object.entries(override || {})) {
    if (
      value &&
      typeof value === 'object' &&
      !Array.isArray(value) &&
      result[key] &&
      typeof result[key] === 'object' &&
      !Array.isArray(result[key])
    ) {
      result[key] = deepMergeMessages(result[key], value);
    } else {
      result[key] = value;
    }
  }
  return result;
}

function flatten(obj, prefix = '', out = {}) {
  for (const [key, value] of Object.entries(obj || {})) {
    const nextKey = prefix ? `${prefix}.${key}` : key;
    if (value && typeof value === 'object' && !Array.isArray(value)) {
      flatten(value, nextKey, out);
    } else if (typeof value === 'string') {
      out[nextKey] = value;
    }
  }
  return out;
}

/** Blatt-Keys, die in `override` explizit als String gesetzt sind */
function overriddenLeafKeys(override, prefix = '', out = new Set()) {
  if (!override || typeof override !== 'object' || Array.isArray(override)) return out;
  for (const [key, value] of Object.entries(override)) {
    const p = prefix ? `${prefix}.${key}` : key;
    if (value && typeof value === 'object' && !Array.isArray(value)) {
      overriddenLeafKeys(value, p, out);
    } else if (typeof value === 'string') {
      out.add(p);
    }
  }
  return out;
}

function topPrefixes(keys, n = 12) {
  const counts = {};
  for (const k of keys) {
    const root = k.split('.')[0];
    counts[root] = (counts[root] || 0) + 1;
  }
  return Object.entries(counts)
    .sort((a, b) => b[1] - a[1])
    .slice(0, n);
}

function parseArgs(argv) {
  let top = 15;
  let locale = null;
  for (let i = 2; i < argv.length; i++) {
    if (argv[i] === '--top' && argv[i + 1]) {
      top = parseInt(argv[++i], 10) || 15;
    } else if (argv[i] === '--locale' && argv[i + 1]) {
      locale = argv[++i];
    }
  }
  return { top, locale };
}

function analyzeLocale(de, deFlat, deKeys, enUSMergedFlat, code) {
  const loc = JSON.parse(fs.readFileSync(path.join(SOURCE_DIR, `${code}.json`), 'utf8'));
  const merged = flatten(deepMergeMessages(JSON.parse(JSON.stringify(de)), loc));
  const explicit = overriddenLeafKeys(loc);

  const inheritedKeys = [];
  const explicitDeKeys = [];
  const enLeakKeys = [];

  for (const k of deKeys) {
    if (merged[k] === deFlat[k]) {
      if (!explicit.has(k)) inheritedKeys.push(k);
      else explicitDeKeys.push(k);
    } else if (merged[k] === enUSMergedFlat[k]) {
      enLeakKeys.push(k);
    }
  }

  return {
    code,
    totalDe: deKeys.length,
    explicitLeaves: explicit.size,
    inheritedFromDe: inheritedKeys.length,
    explicitStillDe: explicitDeKeys.length,
    differsFromDe: deKeys.length - inheritedKeys.length - explicitDeKeys.length,
    enUsLeak: enLeakKeys.length,
    inheritedKeys,
    explicitDeKeys,
    enLeakKeys,
  };
}

function main() {
  const { top, locale: onlyLocale } = parseArgs(process.argv);

  const de = JSON.parse(fs.readFileSync(path.join(SOURCE_DIR, 'de.json'), 'utf8'));
  const deFlat = flatten(de);
  const deKeys = Object.keys(deFlat).sort();

  const enUS = JSON.parse(fs.readFileSync(path.join(SOURCE_DIR, 'en-US.json'), 'utf8'));
  const enUSMergedFlat = flatten(deepMergeMessages(JSON.parse(JSON.stringify(de)), enUS));

  const files = fs
    .readdirSync(SOURCE_DIR)
    .filter((f) => f.endsWith('.json') && !f.endsWith('.backup'))
    .sort();

  const rows = [];
  for (const file of files) {
    if (file === 'de.json') continue;
    const code = path.basename(file, '.json');
    if (onlyLocale && code !== onlyLocale) continue;
    rows.push(analyzeLocale(de, deFlat, deKeys, enUSMergedFlat, code));
  }

  console.log(`Basis: de.json → ${deKeys.length} Blatt-Strings (flatten)\n`);
  console.log(
    [
      'Locale'.padEnd(14),
      'explicit'.padStart(8),
      'erbtsDE'.padStart(8),
      '=de expl'.padStart(10),
      '≠de'.padStart(6),
      '≈en-US'.padStart(8),
    ].join(' '),
  );
  console.log('-'.repeat(58));

  for (const r of rows.sort((a, b) => b.inheritedFromDe - a.inheritedFromDe)) {
    console.log(
      [
        r.code.padEnd(14),
        String(r.explicitLeaves).padStart(8),
        String(r.inheritedFromDe).padStart(8),
        String(r.explicitStillDe).padStart(10),
        String(r.differsFromDe).padStart(6),
        String(r.enUsLeak).padStart(8),
      ].join(' '),
    );
  }

  if (onlyLocale && rows.length === 1) {
    const r = rows[0];
    console.log(`\n--- Top-Namespace (erbte deutsche Strings) für ${r.code} ---`);
    for (const [p, c] of topPrefixes(r.inheritedKeys, top)) {
      console.log(`  ${p}: ${c}`);
    }
    if (r.enLeakKeys.length) {
      console.log(`\n--- Beispiel en-US-Leaks (erste ${Math.min(12, r.enLeakKeys.length)}) ---`);
      for (const k of r.enLeakKeys.slice(0, 12)) {
        console.log(`  ${k}`);
      }
    }
  }

  console.log('\nLegende: erbtsDE = kein Blatt-Override in Locale-JSON (= deutsch); ≈en-US = Wert wie en-US-Merge');
}

main();
