// calc.jsx — Moteur de calcul RAYCAST v2 — modèle forfaits Larks v16
// Sources de vérité :
//   • Larks forfaits (cout/vendant) → coût installation électrique
//   • Tarification_Solaire ($/W) → prix vente solaire/batterie
//   • Tesla mPower, Franken Solar → coût matériel primaire

const TPS_RATE = 0.05000;
const TVQ_RATE = 0.09975;

// ============ FORMATTERS ============
const fmtCAD = (n, decimals = 2) => {
  if (n == null || isNaN(n) || !isFinite(n)) return "—";
  const sign = n < 0 ? "-" : "";
  const abs = Math.abs(n);
  const parts = abs.toFixed(decimals).split(".");
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, "\u00A0");
  return sign + parts.join(",") + "\u00A0$";
};
const fmtCAD0 = (n) => fmtCAD(n, 0);
const fmtPct = (n, decimals = 1) => {
  if (n == null || isNaN(n) || !isFinite(n)) return "—";
  return n.toFixed(decimals).replace(".", ",") + "\u00A0%";
};
const fmtNum = (n, decimals = 0) => {
  if (n == null || isNaN(n) || !isFinite(n)) return "—";
  const parts = n.toFixed(decimals).split(".");
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, "\u00A0");
  return parts.join(",");
};

// ============ LOOKUPS ============
function lookupBatteryPack(brand, presetId) {
  const list = window.RAYCOM_DATA.battery_packs[brand] || [];
  return list.find(p => p.id === presetId) || list[0] || null;
}

function getLarksBrand(battery_brand) {
  const b = window.RAYCOM_DATA.battery_brands.find(x => x.key === battery_brand);
  return b?.larks_brand || "fox_sig";
}

// Sélectionne le forfait Larks v16 selon la config
// Retourne { code, label, cost, sell, larks_eligible }
function pickLarksForfait({ battery_brand, onds, has_battery, has_dim, has_backup, roof_flat, building }) {
  const D = window.RAYCOM_DATA;
  const larks_brand = getLarksBrand(battery_brand);
  // Tesla : Solo/Duo (whole home par défaut) ou Plus (avec sub-panel critiques)
  const isTesla = battery_brand === "tesla";
  let candidates = D.larks_forfaits.filter(f => f.brand === larks_brand);

  // Backup mode mapping
  //   - Tesla : sans backup partiel → Solo/Duo (sell par défaut, whole home via Gateway)
  //            avec backup partiel → Solo Plus / Duo Plus
  //            whole home → Solo/Duo (déjà inclus)
  //   - Fox/Sig : Lite (sans DIM sans backup), HQ (DIM seul), Backup (sub-panel sans DIM), Sécurité (DIM + sub-panel)
  let want_backup, want_dim;
  if (isTesla) {
    want_backup = has_backup === "partial";  // Plus si charges critiques
    want_dim = true;  // toujours Gateway 3
  } else {
    want_backup = has_battery || has_backup === "partial" || has_backup === "whole_home";
    want_dim = has_dim || has_backup === "whole_home";
  }

  let forfait = candidates.find(f =>
    f.onds === Math.min(2, onds) &&
    f.backup === want_backup &&
    f.dim === want_dim
  );

  // Fallback : prend le plus proche
  if (!forfait) {
    forfait = candidates.find(f => f.onds === Math.min(2, onds)) || candidates[0];
  }

  // 3e onduleur (Fox/Sig) : on prend le forfait Duo + OPT-3E-OND
  let extra_inv = null;
  if (!isTesla && onds >= 3) {
    extra_inv = D.larks_options["OPT-3E-OND"];
  }
  // Triple Tesla : Tesla Duo + 1 PW3 supplément matériel
  // (le triple Larks n'existe pas sur la grille publique — on ajoute un supplément install équivalent OPT-3E-OND)
  if (isTesla && onds >= 3) {
    extra_inv = { ...D.larks_options["OPT-3E-OND"], label: "3e PW3 — supplément install Tesla Triple" };
  }

  // Larks éligibilité : seulement si toit angle + Fox/Sig (Tesla = Raycom direct, mais barème Tesla figure quand même dans la grille v16 → admissible)
  // Larks ne couvre pas les toits plats / commercial → on garde forfait Tesla/Fox/Sig comme baseline (Raycom-direct exécute)
  const larks_eligible = !roof_flat && !["com_petit", "com_moyen", "com_grand"].includes(building);

  return {
    ...forfait,
    extra_inv,
    larks_eligible,
    cost_total: forfait.cost + (extra_inv?.cost || 0),
    sell_total: forfait.sell + (extra_inv?.sell || 0)
  };
}

// Zone deplacement selon région
function getZoneCost(region_id) {
  const D = window.RAYCOM_DATA;
  const region = D.regions.find(r => r.key === region_id);
  if (!region) return { cost: 0, sell: 0, key: "Z1" };
  const zone = D.zones.find(z => z.key === region.zone);
  return zone || { cost: 0, sell: 0, key: "Z1" };
}

// === Matrice $/W ===
function getRateForKW({ building, roof, with_battery }) {
  const D = window.RAYCOM_DATA;
  const row = D.rates_solar.find(r => r.building === building && r.roof === roof);
  if (!row) return with_battery ? 4.20 : 3.10;  // fallback résid asphalte
  return with_battery ? row.with_batt : row.no_batt;
}

// Onduleurs nécessaires selon kW (limite Larks 12 kW/onduleur)
function inverterCountFromKW(kw) {
  if (kw <= 0) return 1;
  return Math.max(1, Math.ceil(kw / 12));
}

// ============ CALCUL COÛTS ============
function calcSolarCosts(state) {
  const D = window.RAYCOM_DATA;
  const { solar, project_type, building_type } = state;
  const roof = D.roof_types.find(r => r.key === solar.roof_type);
  const bldg = D.building_types.find(b => b.key === building_type);

  // === Borne VÉ ===
  if (project_type === "borne") {
    return { panels: 0, racking: 0, wiring: 0, inverter: 0, batteries: 0, install: 0, backup_extra: 0,
             forfait: null, total: 0, breakdown: [] };
  }

  // === Batterie seule ===
  if (project_type === "batterie") {
    const pack = lookupBatteryPack(solar.battery_brand, solar.battery_preset_id);
    if (!pack) return zeroCosts();
    const isTesla = solar.battery_brand === "tesla";
    const forfait = pickLarksForfait({
      battery_brand: solar.battery_brand,
      onds: pack.onds,
      has_battery: true,
      has_dim: true,
      has_backup: solar.backup_mode,
      roof_flat: false,
      building: building_type
    });
    const tesla_extras = isTesla ? (D.tesla_skus.bag.cost + D.tesla_skus.rmm.cost) : 0;
    const cost_inv = pack.cost_inv + (isTesla ? 0 : 0); // already includes inverter
    const cost_batt = pack.cost_batt;
    const cost_install = forfait.cost_total;
    return {
      panels: 0, racking: 0, wiring: 0,
      inverter: cost_inv,
      batteries: cost_batt + tesla_extras,
      install: cost_install,
      backup_extra: 0,
      forfait,
      pack,
      total: cost_inv + cost_batt + tesla_extras + cost_install,
      breakdown: makeBreakdown({ panels: 0, racking: 0, wiring: 0, inv: cost_inv, batt: cost_batt + tesla_extras, install: cost_install })
    };
  }

  // === Solaire (avec ou sans batterie) ===
  const watts = solar.kw * 1000;
  const panel_count = Math.ceil(watts / D.panel_ref.watts);

  // 1. Panneaux
  const cost_panels = panel_count * D.panel_ref.cost;

  // 2. Racking (par tranche de 10)
  const racking_kits = Math.ceil(panel_count / 10);
  const cost_racking = racking_kits * D.racking_per_10.cost;

  // 3. Wiring + protections + combiner + monitoring
  const cost_wiring = D.solar_wiring_kit.cost + D.solar_protections.cost + D.combiner_kit.cost + D.monitoring_kit.cost;

  // 4. Onduleur / Batterie selon marque
  const has_battery = project_type === "solaire_batterie" && solar.battery_brand !== "none";
  let cost_inv = 0, cost_batt = 0, pack = null, tesla_extras = 0;

  if (has_battery) {
    pack = lookupBatteryPack(solar.battery_brand, solar.battery_preset_id);
    if (pack) {
      cost_inv = pack.cost_inv;
      cost_batt = pack.cost_batt;
      if (solar.battery_brand === "tesla") tesla_extras = D.tesla_skus.bag.cost + D.tesla_skus.rmm.cost;
    }
  } else {
    // Sans batterie : onduleur string générique selon kW
    const inv_count = inverterCountFromKW(solar.kw);
    cost_inv = inv_count * D.generic_hybrid_inverter.cost;
  }

  // 5. Onduleurs onds — pour Larks forfait
  const onds = has_battery ? (pack?.onds || 1) : inverterCountFromKW(solar.kw);

  // 6. Larks forfait install (1/2/3 onduleurs ±DIM ±backup)
  const forfait = pickLarksForfait({
    battery_brand: solar.battery_brand,
    onds,
    has_battery,
    has_dim: solar.with_dim || has_battery,
    has_backup: solar.backup_mode,
    roof_flat: roof?.flat,
    building: building_type
  });

  let cost_install = forfait.cost_total;

  // 7. Backup extra (whole home avec entrée > 200 A = ATS commercial)
  let cost_backup_extra = 0;
  if (solar.backup_mode === "whole_home" && solar.service_amperage > 200) {
    cost_backup_extra = 1200;
  }

  // 8. Surcharge toiture plate / commerciale (Larks ne fait pas) — uplift install 30-60 %
  if (roof?.flat || ["com_petit","com_moyen","com_grand"].includes(building_type)) {
    const uplift_factor = {
      com_grand: 1.85, com_moyen: 1.60, com_petit: 1.40
    }[building_type] || 1.30; // flat-roof residentiel/multilog
    cost_install = cost_install * uplift_factor;
  }

  // 9. Ingénierie OIQ + permis HQ (toujours)
  const cost_oiq = D.oiq_seal.cost + D.hq_permit.cost;

  const total = cost_panels + cost_racking + cost_wiring + cost_inv + cost_batt + tesla_extras + cost_install + cost_backup_extra + cost_oiq;

  return {
    panels: cost_panels,
    racking: cost_racking,
    wiring: cost_wiring + cost_oiq, // bundle dans "wiring/admin"
    inverter: cost_inv,
    batteries: cost_batt + tesla_extras,
    install: cost_install,
    backup_extra: cost_backup_extra,
    forfait, pack, panel_count, onds,
    total,
    breakdown: makeBreakdown({ panels: cost_panels, racking: cost_racking, wiring: cost_wiring + cost_oiq, inv: cost_inv, batt: cost_batt + tesla_extras, install: cost_install + cost_backup_extra })
  };
}

function zeroCosts() {
  return { panels: 0, racking: 0, wiring: 0, inverter: 0, batteries: 0, install: 0, backup_extra: 0, forfait: null, total: 0, breakdown: [] };
}

function makeBreakdown({ panels, racking, wiring, inv, batt, install }) {
  return [
    { label: "🌞 Panneaux + racking", value: panels + racking },
    { label: "🔌 Onduleur / AIO", value: inv },
    { label: "🔋 Batteries", value: batt },
    { label: "🛠 Install Raycom (forfait + admin)", value: install + wiring }
  ].filter(x => x.value > 0);
}

// Bornes VÉ — coût + vente
function calcBornePrix(state) {
  const D = window.RAYCOM_DATA;
  const { ve, options } = state;
  let borne_cost = 0, borne_sell = 0;
  const list = ve.bornes_selected || [];
  list.forEach(sel => {
    const b = D.bornes.find(x => x.key === sel.key);
    if (!b) return;
    borne_cost += b.cost * sel.qty;
    borne_sell += b.sell * sel.qty;
  });
  const totalQty = list.reduce((s, x) => s + x.qty, 0);

  // Install
  const install = ve.scenario_post_2018 ? D.borne_install.prefilage : D.borne_install.fil_neuf;
  const install_cost = install.cost * totalQty;
  const install_sell = install.sell * totalQty;

  // Câble extra
  const cable_extra_m = Math.max(0, (ve.cable_length_pi - 50)) * 0.3048; // pi → m
  const cable_cost = cable_extra_m * D.borne_install.cable_extra.cost;
  const cable_sell = cable_extra_m * D.borne_install.cable_extra.sell;

  // DCC
  let dcc_cost = 0, dcc_sell = 0;
  if (ve.dcc_option === "dcc12") { dcc_cost = D.borne_install.dcc12.cost; dcc_sell = D.borne_install.dcc12.sell; }
  else if (ve.dcc_option === "dsdc") { dcc_cost = D.borne_install.dsdc.cost; dcc_sell = D.borne_install.dsdc.sell; }

  // Options
  const opt_cost = (options || []).reduce((s, o) => s + (o.cost || 0), 0);
  const opt_sell = (options || []).reduce((s, o) => s + (o.sell || 0), 0);

  const cost = borne_cost + install_cost + cable_cost + dcc_cost + opt_cost;
  const sell = borne_sell + install_sell + cable_sell + dcc_sell + opt_sell;

  return { cost, sell, qty: totalQty, borne_cost, borne_sell, install_cost, install_sell, dcc_cost, dcc_sell };
}

// ============ PRIX VENTE ============
function calcPrixVente(state, costs) {
  const D = window.RAYCOM_DATA;
  const { project_type, building_type, solar } = state;
  const bldg = D.building_types.find(b => b.key === building_type);

  if (project_type === "borne") {
    // Sum-of-parts (catalog sell) vs marge cible — prendre le max
    const b = calcBornePrix(state);
    const prix_marge = costs.total / (1 - bldg.margin);
    return Math.max(b.sell, prix_marge);
  }
  if (project_type === "batterie") {
    // Pas de matrice $/W — formule marge cible
    return costs.total / (1 - bldg.margin);
  }

  // Solaire : matrice $/W vs marge cible — prendre le max
  const has_battery = solar.battery_brand !== "none" && project_type === "solaire_batterie";
  const rate = getRateForKW({ building: building_type, roof: solar.roof_type, with_battery: has_battery });
  const prix_matrice = rate * solar.kw * 1000;
  const prix_marge = costs.total / (1 - bldg.margin);
  return Math.max(prix_matrice, prix_marge);
}

// ============ PRODUCTION SOLAIRE ============
function calcProduction(state) {
  const D = window.RAYCOM_DATA;
  const { solar, client } = state;
  const region = D.regions.find(r => r.key === client.region_id) || D.regions[0];
  const orientationFactor = { "S": 1.00, "SE": 0.96, "SO": 0.96, "E": 0.86, "O": 0.86, "NE": 0.76, "NO": 0.76, "N": 0.68 }[client.orientation] || 0.96;
  const roof = D.roof_types.find(r => r.key === solar.roof_type);
  const slopeFactor = roof?.flat ? 0.92 : (solar.slope >= 4 && solar.slope <= 8 ? 1.00 : 0.95);
  return Math.round(solar.kw * region.kwh_per_kw * orientationFactor * slopeFactor);
}

// ============ ÉCONOMIES ============
function calcSavings(state, production_kwh) {
  const D = window.RAYCOM_DATA;
  const { client, solar } = state;
  const tariff = D.hq_tariffs.find(t => t.id === client.hq_tariff) || D.hq_tariffs[0];
  const base = (production_kwh * tariff.rate) / 100;
  let peak_bonus = 0;
  if ((client.hq_tariff === "FlexD" || client.hq_tariff === "DP") && solar.battery_brand !== "none") {
    const pack = lookupBatteryPack(solar.battery_brand, solar.battery_preset_id);
    const battKwh = pack?.kwh || 0;
    const bill = client.bill_input_mode === "dollars" ? (client.annual_hq_bill || 0) : ((client.annual_hq_kwh || 0) * tariff.rate / 100);
    peak_bonus = bill * Math.min(0.15, battKwh / 100);
  }
  let v2x = 0;
  if (solar.with_v2x) {
    const ev_kwh = ((client.ev_km_per_year || 0) * (client.ev_kwh_per_100km || 0)) / 100;
    v2x = ev_kwh * 0.15;
  }
  return { base, peak_bonus, v2x, total: base + peak_bonus + v2x };
}

// ============ SUBVENTIONS ============
function calcLogisVert(state, ht_eligible) {
  if (state.project_type === "borne" || state.project_type === "batterie") return 0;
  const by_kw = state.solar.kw * 1000;
  const cap_40 = ht_eligible * 0.40;
  return Math.min(by_kw, cap_40);
}

function calcRoulezVert(state) {
  if (state.project_type !== "borne") return 0;
  const totalQty = (state.ve.bornes_selected || []).reduce((s, x) => s + x.qty, 0);
  const building = state.building_type;
  if (building === "residentiel_unifam") return Math.min(1, totalQty) * 600;
  if (building === "multilog") return Math.min(5000, totalQty * 600);
  return 0;
}

// ============ FINANCEIT ============
function calcFinanceit(state, ttc) {
  const D = window.RAYCOM_DATA;
  const plan = D.financeit_plans.find(p => p.id === state.client.financing_plan_id);
  if (!plan || plan.id === "comptant") return { plan, monthly: 0, biweekly: 0, weekly: 0, total_interest: 0, fee: 0 };
  const principal = Math.max(0, ttc - 1000);
  const r = plan.apr / 100 / 12;
  let monthly;
  if (r === 0) monthly = plan.months > 0 ? principal / plan.months : 0;
  else monthly = principal * r * Math.pow(1 + r, plan.months) / (Math.pow(1 + r, plan.months) - 1);
  if (!isFinite(monthly)) monthly = 0;
  return {
    plan, monthly,
    biweekly: (monthly * 12) / 26,
    weekly: (monthly * 12) / 52,
    total_interest: monthly * plan.months - principal,
    fee: ttc * (plan.fee_pct / 100)
  };
}

// ============ COMMISSION + MARGE ============
function commissionRate(margePct) {
  if (margePct < 30) return { rate: 2.5, label: "Plancher" };
  if (margePct < 35) return { rate: 3.5, label: "Acceptable" };
  if (margePct < 40) return { rate: 4.0, label: "Cible" };
  return { rate: 5.0, label: "Bonus" };
}

function margeColor(margePct, building) {
  if (building === "com_grand") {
    if (margePct >= 25) return "green";
    if (margePct >= 18) return "yellow";
    return "red";
  }
  if (margePct >= 35) return "green";
  if (margePct >= 30) return "yellow";
  return "red";
}

// ============ MASTER ============
function fullCalc(state) {
  const D = window.RAYCOM_DATA;
  const costs = calcSolarCosts(state);

  // Bornes
  if (state.project_type === "borne") {
    const b = calcBornePrix(state);
    costs.total = b.cost;
    costs.breakdown = [
      { label: "🔌 Bornes (matériel)", value: b.borne_cost },
      { label: "🛠 Installation Raycom", value: b.install_cost },
      { label: "📟 DCC/DSDC", value: b.dcc_cost }
    ].filter(x => x.value > 0);
  }

  // Options solaires (déjà non-comptées pour borne via calcBornePrix)
  let extra_cost = 0, extra_sell = 0;
  if (state.project_type !== "borne") {
    extra_cost = (state.options || []).reduce((s, o) => s + (o.cost || 0), 0);
    extra_sell = (state.options || []).reduce((s, o) => s + (o.sell || 0), 0);
  }
  const cost_total_pre_zone = costs.total + extra_cost;

  // Zone déplacement
  const zone = getZoneCost(state.client?.region_id);
  const cost_total = cost_total_pre_zone + zone.cost;

  // Prix vente (matrice $/W ou marge cible — max des deux)
  const ht_base = calcPrixVente(state, { total: cost_total });
  // Ajouter zone.sell + extras au HT, puis re-appliquer marge plancher
  const bldg2 = D.building_types.find(b => b.key === state.building_type);
  let ht_with_extras = ht_base + extra_sell + zone.sell;
  const ht_plancher = cost_total / (1 - bldg2.margin);
  const ht = Math.max(ht_with_extras, ht_plancher);

  // Taxes
  const tps = ht * TPS_RATE;
  const tvq = ht * TVQ_RATE;
  const ttc = ht + tps + tvq;

  // Production / économies
  const hasSolar = state.project_type === "solaire" || state.project_type === "solaire_batterie";
  const production = hasSolar ? calcProduction(state) : 0;
  const savings = hasSolar ? calcSavings(state, production) : { base: 0, peak_bonus: 0, v2x: 0, total: 0 };

  // Subventions
  const logisvert = calcLogisVert(state, ht);
  const rve = calcRoulezVert(state);
  const total_grant = logisvert + rve;

  // Financement
  const fin = calcFinanceit(state, ttc);
  const net_cost = Math.max(0, ttc - total_grant);
  const payback = savings.total > 0 ? net_cost / savings.total : Infinity;
  const savings_25 = savings.total * 25 * 0.90;

  // Marge
  const marge_brute = ht - cost_total;
  const marge_pct = ht > 0 ? (marge_brute / ht) * 100 : 0;
  const marge_ajustee = marge_brute - fin.fee;
  const marge_aj_pct = ht > 0 ? (marge_ajustee / ht) * 100 : 0;
  const com = commissionRate(marge_aj_pct);
  const commission = ht * (com.rate / 100);

  // Couverture facture
  const tariff = D.hq_tariffs.find(t => t.id === state.client.hq_tariff) || D.hq_tariffs[0];
  const annual_kwh = state.client.bill_input_mode === "dollars"
    ? ((state.client.annual_hq_bill || 0) * 100 / tariff.rate)
    : (state.client.annual_hq_kwh || 0);
  const coverage_pct = annual_kwh > 0 ? Math.min(150, (production / annual_kwh) * 100) : 0;

  // Autonomie
  const pack = lookupBatteryPack(state.solar.battery_brand, state.solar.battery_preset_id);
  const batt_kwh = pack?.kwh || 0;
  const daily_consumption = annual_kwh / 365;
  const autonomy_days = daily_consumption > 0 && batt_kwh > 0 ? batt_kwh / (daily_consumption * 0.2) : 0;

  // Valeur maison (≈ 55 % du TTC)
  const home_value = ttc * 0.55;

  return {
    costs, cost_total, zone, extra_cost, extra_sell,
    ht, tps, tvq, ttc,
    production, savings,
    logisvert, rve, total_grant,
    fin, net_cost, payback, savings_25,
    marge_brute, marge_pct, marge_ajustee, marge_aj_pct, com, commission,
    coverage_pct, autonomy_days, home_value,
    annual_kwh, pack
  };
}

window.RAYCAST_CALC = {
  fmtCAD, fmtCAD0, fmtPct, fmtNum,
  fullCalc, calcBornePrix, lookupBatteryPack, commissionRate, margeColor,
  pickLarksForfait, getZoneCost, getRateForKW, inverterCountFromKW
};
