#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FRANCE 2027 — Moteur de trajectoires budgétaires V1 (comptable, multi-scénarios)
Usage : python3 moteur.py [--params parametres.json] [--out resultats/]

Mécanique annuelle (2026-2037), base 2025 :
  PIB(t)        = PIB(t-1) × (1+vol+boost) × (1+inflation)
  Recettes(t)   = part_recettes_2025 × PIB(t) × élasticité + leviers_recettes(t)
  Dép. prim.(t) = dép.prim.(t-1) × (1+inflation) × (1+dérive_volume) + Δleviers_dépenses(t) + dépenses_crise/guerre(t)
  Taux apparent(t) = taux(t-1) + part_refi × (OAT(t) − taux(t-1))
  Charge intérêts(t) = taux apparent(t) × dette(t-1)
  Solde(t)      = Recettes − Dépenses ;  Dette(t) = Dette(t-1) − Solde(t)
Les leviers sont en Mds€ 2025, indexés sur le niveau des prix du scénario.
"""
import json, csv, os, argparse

ANNEES = list(range(2026, 2038))

def val(d, annee):
    """Lit une valeur annuelle dans un dict {"2028": x, "defaut": y}."""
    if d is None:
        return 0.0
    if str(annee) in d:
        return float(d[str(annee)])
    if "defaut" in d:
        # avant la première année définie, un levier vaut 0 (pas encore activé)
        annees_def = [int(k) for k in d if k.isdigit()]
        if annees_def and annee < min(annees_def):
            return 0.0
        return float(d["defaut"])
    return 0.0

def simulate(params, nom_scenario):
    sc = params["scenarios"][nom_scenario]
    base = params["base_2025"]
    hyp = params["hypotheses_tendancielles"]
    leviers = params["leviers_programme"]
    facteur = float(sc.get("facteur_leviers", 1.0))
    suspendus = sc.get("leviers_suspendus_depuis_2029", [])

    pib = base["pib_nominal_mds"]
    part_recettes = base["recettes_mds"] / pib
    dep_primaires = base["depenses_mds"] - base["charge_interets_mds"]
    dette = base["dette_mds"]
    taux_apparent = base["charge_interets_mds"] / base["dette_mds"]
    niveau_prix = 1.0
    delta_d29_prec = 0.0  # délai d'1 an sur le retour fiscal de la conditionnalité
    rows = []

    for an in ANNEES:
        vol = val(sc["croissance_volume_pct"], an) / 100
        boost = val(sc.get("boost_croissance_reformes_pct"), an) / 100 * (1 if facteur > 0 else 0)
        infl = val(sc["inflation_pct"], an) / 100
        oat = val(sc["oat_pct"], an) / 100

        croissance_totale = vol + boost
        pib = pib * (1 + croissance_totale) * (1 + infl)
        niveau_prix *= (1 + infl)

        # --- leviers (cumulés, Mds 2025 -> indexés prix) ---
        lev_rec, lev_dep = 0.0, 0.0
        delta_d29 = 0.0
        for nom, lv in leviers.items():
            if nom in suspendus and an >= 2029:
                # gelé au niveau atteint en 2028
                delta = val(lv["deltas_cumules"], 2028)
            else:
                delta = val(lv["deltas_cumules"], an)
            delta *= facteur * niveau_prix
            if lv["type"] == "recettes":
                lev_rec += delta
            else:
                lev_dep += delta
            if nom == "impots_production_suppression":
                delta_d29 = delta

        # retour fiscal de la conditionnalité (50% salaires / 50% compétitivité-invest),
        # appliqué avec 1 an de décalage sur le montant supprimé l'année précédente
        retour = -delta_d29_prec * hyp.get("taux_retour_impots_production", 0.0)
        lev_rec += retour
        delta_d29_prec = delta_d29

        dep_exceptionnelles = (val(sc.get("depenses_crise_mds"), an)
                               + val(sc.get("defense_guerre_mds"), an)) * niveau_prix

        # --- recettes ---
        elast = hyp["elasticite_recettes"]
        recettes = part_recettes * pib * elast + lev_rec

        # --- dépenses primaires tendancielles ---
        derive = hyp["croissance_volume_depenses_primaires_pct"] / 100
        dep_primaires = dep_primaires * (1 + infl) * (1 + derive)
        depenses_primaires_totales = dep_primaires + lev_dep + dep_exceptionnelles

        # --- charge d'intérêts ---
        taux_apparent = taux_apparent + hyp["part_refinancement_annuelle"] * (oat - taux_apparent)
        charge = taux_apparent * dette

        depenses = depenses_primaires_totales + charge
        solde = recettes - depenses
        dette = dette - solde

        rows.append({
            "annee": an,
            "pib_mds": round(pib, 1),
            "croissance_vol_pct": round(croissance_totale * 100, 2),
            "inflation_pct": round(infl * 100, 2),
            "recettes_mds": round(recettes, 1),
            "recettes_pct_pib": round(recettes / pib * 100, 1),
            "depenses_mds": round(depenses, 1),
            "depenses_pct_pib": round(depenses / pib * 100, 1),
            "dont_charge_interets_mds": round(charge, 1),
            "taux_apparent_pct": round(taux_apparent * 100, 2),
            "solde_mds": round(solde, 1),
            "solde_pct_pib": round(solde / pib * 100, 1),
            "dette_mds": round(dette, 1),
            "dette_pct_pib": round(dette / pib * 100, 1),
            "leviers_recettes_mds": round(lev_rec, 1),
            "leviers_depenses_mds": round(lev_dep, 1),
            "depenses_exceptionnelles_mds": round(dep_exceptionnelles, 1),
        })
    return rows

def main():
    ap = argparse.ArgumentParser()
    here = os.path.dirname(os.path.abspath(__file__))
    ap.add_argument("--params", default=os.path.join(here, "parametres.json"))
    ap.add_argument("--out", default=os.path.join(here, "resultats"))
    args = ap.parse_args()

    with open(args.params) as f:
        params = json.load(f)
    os.makedirs(args.out, exist_ok=True)

    synthese = []
    for nom in params["scenarios"]:
        rows = simulate(params, nom)
        path = os.path.join(args.out, f"trajectoire_{nom}.csv")
        with open(path, "w", newline="") as f:
            w = csv.DictWriter(f, fieldnames=rows[0].keys())
            w.writeheader()
            w.writerows(rows)
        r32 = next(r for r in rows if r["annee"] == 2032)
        r37 = rows[-1]
        croiss_moy = sum(r["croissance_vol_pct"] for r in rows if 2027 <= r["annee"] <= 2032) / 6
        synthese.append({
            "scenario": nom,
            "solde_2032_pct": r32["solde_pct_pib"],
            "dette_2032_pct": r32["dette_pct_pib"],
            "depenses_2032_pct": r32["depenses_pct_pib"],
            "croissance_moy_2027_2032": round(croiss_moy, 2),
            "charge_interets_2032_mds": r32["dont_charge_interets_mds"],
            "solde_2037_pct": r37["solde_pct_pib"],
            "dette_2037_pct": r37["dette_pct_pib"],
            "depenses_2037_pct": r37["depenses_pct_pib"],
            "charge_interets_2037_mds": r37["dont_charge_interets_mds"],
        })

    with open(os.path.join(args.out, "synthese_scenarios.csv"), "w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=synthese[0].keys())
        w.writeheader()
        w.writerows(synthese)

    c = params["cibles_2032"]
    print(f"{'scénario':28s} {'solde32':>8s} {'dette32':>8s} {'dép.32':>7s} {'croi.moy':>8s} | cibles : solde {c['deficit_pct_pib']} / dette {c['dette_pct_pib']} / dép {c['depenses_pct_pib']}")
    for s in synthese:
        print(f"{s['scenario']:28s} {s['solde_2032_pct']:8.1f} {s['dette_2032_pct']:8.1f} {s['depenses_2032_pct']:7.1f} {s['croissance_moy_2027_2032']:8.2f}")
    print("\nFichiers écrits dans", args.out)

if __name__ == "__main__":
    main()
