// gp-core.jsx — APPLICATION CORE (couche transversale, sans UI)
// ════════════════════════════════════════════════════════════════
//  Responsabilités : valider le registre au chargement, et fournir
//  la frontière de persistance versionnée. AUCUN composant React ici.
//  Tout est pur et testable. Charge après gp-data + gp-registry.
// ════════════════════════════════════════════════════════════════
const { ALL_MODULES: COR_MODS, RECIPES: COR_RCP, SPEC_OVERRIDES: COR_SPECS, DEFAULT_RECIPE_CHOICES: COR_DEF_CHOICES, PROJECTS: COR_PROJECTS } = window;

const SCHEMA_VERSION = 4;
// Clé stable (indépendante de la version) — la version est portée par schemaVersion interne.
// Lors de la migration v3→v4, on lit l'ancienne clé versionnée puis on écrit sur la clé stable.
const STORE_KEY = 'gp_estimateur_state';
const STORE_KEY_V3 = 'gp_estimateur_state_v3'; // clé legacy à lire une seule fois pour migration

// ── Validation du registre (échoue tôt, pas en production) ───────
function validateRegistry() {
  const issues = [];
  const ids = new Set(COR_MODS.map(m => m.id));

  COR_MODS.forEach(m => {
    if (!m.id)   issues.push({ level:'error', where:'module', msg:`module sans id (${m.name||'?'})` });
    if (!m.icon) issues.push({ level:'warn',  where:m.id, msg:`module « ${m.name} » sans icône` });
  });

  COR_RCP.forEach(r => {
    if (!ids.has(r.module))
      issues.push({ level:'error', where:r.id, msg:`recette « ${r.name} » pointe vers un module introuvable : ${r.module}` });
    if (!r.lines || !r.lines.length)
      issues.push({ level:'warn', where:r.id, msg:`recette « ${r.name} » sans composant` });
    (r.lines||[]).forEach(l => {
      if (!['area','perim','units','prev'].includes(l.driver))
        issues.push({ level:'error', where:r.id, msg:`pilote inconnu « ${l.driver} » sur ${l.item}` });
      if (l.driver !== 'prev' && (!l.base || l.base <= 0))
        issues.push({ level:'error', where:r.id, msg:`base invalide sur ${l.item}` });
    });
  });

  Object.keys(COR_SPECS).forEach(id => {
    if (!ids.has(id)) issues.push({ level:'warn', where:id, msg:`spec orpheline (aucun module ${id})` });
  });

  if (issues.length) {
    console.group('%cGO·PASSIF — validation du registre', 'color:#cf8a2c;font-weight:600');
    issues.forEach(i => console[i.level === 'error' ? 'error' : 'warn'](`[${i.where}] ${i.msg}`));
    console.groupEnd();
  } else {
    console.info('%cGO·PASSIF — registre valide ✓', 'color:#1f9d63');
  }
  return issues;
}

// Recettes invalides (pour badge UI dans l'admin)
function recipeIssues(recipe) {
  const ids = new Set(COR_MODS.map(m => m.id));
  const out = [];
  if (!ids.has(recipe.module)) out.push('Module introuvable');
  if (!recipe.lines || !recipe.lines.length) out.push('Aucun composant');
  return out;
}

// ── Frontière de persistance versionnée ─────────────────────────
function loadState() {
  try {
    // Tenter d'abord la clé stable (v4+)
    let raw = localStorage.getItem(STORE_KEY);
    // Fallback : clé legacy v3 (migration first-run)
    if (!raw) raw = localStorage.getItem(STORE_KEY_V3);
    if (!raw) return null;
    const data = JSON.parse(raw);
    return migrate(data);
  } catch (e) { console.warn('GO·PASSIF — état illisible, réinitialisé', e); return null; }
}

function saveState(state) {
  try {
    const payload = { ...state, schemaVersion: SCHEMA_VERSION, savedAt: Date.now() };
    localStorage.setItem(STORE_KEY, JSON.stringify(payload));
    return payload.savedAt;
  } catch (e) { console.warn('GO·PASSIF — sauvegarde impossible', e); return null; }
}

// ── Migrations entre versions de schéma ──────────────────────────
function migrate(data) {
  if (!data || typeof data !== 'object') return null;

  // v3 → v4 : mesures globales + recipeChoices → byProject[pid]
  if (!data.schemaVersion || data.schemaVersion < 4) {
    const pid = data.activeProjectId || (COR_PROJECTS && COR_PROJECTS[0] && COR_PROJECTS[0].id) || 'p1';
    const oldChoices = data.recipeChoices || COR_DEF_CHOICES || {};

    // Convertir recipeChoices (v3) → moduleConfig (v4) en ajoutant buffer/fields/rows par défaut
    const moduleConfig = {};
    Object.entries(oldChoices).forEach(([mid, ch]) => {
      moduleConfig[mid] = {
        recipe: ch.recipe,
        brand:  ch.brand,
        excl:   ch.excl || {},
        buffer: (COR_SPECS && COR_SPECS[mid] && COR_SPECS[mid].buffer) ?? 8,
        fields: {},
        rows:   [],
      };
    });

    data.byProject = {
      [pid]: {
        measures:     data.measures || [],
        moduleConfig,
      },
    };

    delete data.measures;
    delete data.recipeChoices;
    data.schemaVersion = 4;

    console.info('%cGO·PASSIF — migration v3→v4 effectuée', 'color:#1f9d63');
    // Supprimer l'ancienne clé après migration réussie
    try { localStorage.removeItem(STORE_KEY_V3); } catch (_) {}
  }

  return data;
}

// ── Valeur par défaut d'un moduleConfig pour un projet vierge ────
function defaultModuleConfig(mid) {
  return {
    recipe: null,
    brand:  null,
    excl:   {},
    buffer: (COR_SPECS && COR_SPECS[mid] && COR_SPECS[mid].buffer) ?? 8,
    fields: {},
    rows:   [],
  };
}

// ── Sous-arbre byProject vide pour un nouveau projet ─────────────
function emptyProjectData() {
  return { measures: [], moduleConfig: {} };
}

Object.assign(window, {
  SCHEMA_VERSION, STORE_KEY,
  validateRegistry, recipeIssues,
  loadState, saveState, migrate,
  defaultModuleConfig, emptyProjectData,
});
