export const RETAIL_UI_CATALOG_SCHEMA = 1;

const MODEL_ID = /^model_player_[a-z0-9_]+$/;
const LANG_KEY = /^#str_[0-9]+$/;

function decodeQuoted(value) {
  return String(value).replace(/\\([\\"])/g, '$1');
}

export function parseLanguageTable(source) {
  const strings = new Map();
  for (const line of String(source || '').split(/\r?\n/)) {
    const match = /^\s*"(#[^"]+)"\s+"((?:\\.|[^"])*)"/.exec(line);
    if (match && LANG_KEY.test(match[1])) strings.set(match[1], decodeQuoted(match[2]));
  }
  return strings;
}

export function parsePlayerModels(source, language = new Map()) {
  const models = [];
  const declarations = /(?:^|\n)\s*playerModel\s+(model_player_[a-z0-9_]+)\s*\{([\s\S]*?)\n\s*\}/g;
  for (const match of String(source || '').matchAll(declarations)) {
    const id = match[1];
    if (!MODEL_ID.test(id)) continue;
    const values = new Map();
    for (const line of match[2].split(/\r?\n/)) {
      const pair = /^\s*"([A-Za-z0-9_]+)"\s+"((?:\\.|[^"])*)"/.exec(line);
      if (pair) values.set(pair[1], decodeQuoted(pair[2]));
    }
    const rawTeam = String(values.get('team') || '').toLowerCase();
    const team = rawTeam === 'marine' ? 'Marine' : rawTeam === 'strogg' ? 'Strogg' : null;
    const description = values.get('description') || '';
    const label = language.get(description) || id
      .replace(/^model_player_/, '')
      .split('_')
      .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
      .join(' ');
    models.push({ id, label: label.slice(0, 80), team });
  }
  return [...new Map(models.map((model) => [model.id, model])).values()]
    .sort((left, right) => left.label.localeCompare(right.label, 'en'));
}

export function validateRetailUiCatalog(raw, retailManifest) {
  if (raw?.schema !== RETAIL_UI_CATALOG_SCHEMA || !retailManifest?.revision ||
      !retailManifest?.fingerprint || !Array.isArray(raw?.models)) {
    throw new Error('retail UI catalog does not match the staged retail revision');
  }
  const seen = new Set();
  const models = raw.models.map((entry) => {
    const id = String(entry?.id || '');
    const label = String(entry?.label || '').trim();
    const team = entry?.team === null ? null : String(entry?.team || '');
    if (!MODEL_ID.test(id) || seen.has(id) || !label || label.length > 80 ||
        ![null, 'Marine', 'Strogg'].includes(team)) {
      throw new Error(`invalid player model metadata: ${id || '<unnamed>'}`);
    }
    seen.add(id);
    return Object.freeze({ id, label, team });
  });
  if (!models.length || models.length > 128) throw new Error('retail UI catalog has no valid player models');
  return Object.freeze({
    schema: RETAIL_UI_CATALOG_SCHEMA,
    revision: retailManifest.revision,
    fingerprint: retailManifest.fingerprint,
    models: Object.freeze(models),
  });
}
