"""
Gauge Sync — mappe les dernières données de marché (macro_regime_history)
vers les nœuds manuels des modèles instruments.

Logique :
  - Récupère les derniers gauges (VIX, DXY, US10Y, Brent, LQD, cuivre...)
  - Pour chaque instrument, propose une valeur pour chaque nœud mappable
  - Confidence : HIGH (direct), MEDIUM (dérivé simple), LOW (proxy)
  - L'utilisateur review et confirme dans le frontend
"""
import json
import math
from typing import Optional

# ── Structures de suggestion ───────────────────────────────────────────────────

def _conf(level: str, value: float, node_id: str, label: str,
          unit: str, source: str, note: str) -> dict:
    return {
        "node_id":    node_id,
        "label":      label,
        "value":      round(value, 2),
        "unit":       unit,
        "source":     source,
        "confidence": level,   # HIGH | MEDIUM | LOW
        "note":       note,
    }


# ── Dérivations communes ───────────────────────────────────────────────────────

def _us_real_rate_bps(g: dict) -> float:
    """Taux réel US approximé : 10Y nominal - breakeven estimé.
    TIPS ETF 109.76 → taux TIPS ≈ 4.487% - 2.0% = ~2.0% → 200bps.
    Approximation : 10Y - 2.2% (breakeven historique moyen).
    """
    us10y = g.get("us10y", {}).get("value", 4.5)
    breakeven_est = 2.2  # %
    return (us10y - breakeven_est) * 100  # bps


def _risk_appetite_score(g: dict) -> float:
    """Score appétit risque -5 à +5.
    VIX < 15 = risk-on (+), VIX > 25 = risk-off (−).
    """
    vix = g.get("vix", {}).get("value", 20.0)
    spx_200d = g.get("spx_vs_200d", {}).get("value", 0.0)
    # Base from VIX
    score = 3.0 - vix / 8.0
    # Adjust from SPX vs 200d MA
    score += spx_200d * 0.05
    return max(-5.0, min(5.0, round(score, 2)))


def _ig_spread_bps(g: dict) -> float:
    """Proxy spread IG depuis prix LQD ETF.
    LQD à 109+ = spreads très serrés (~80bps). Chaque point de LQD ≈ 5bps spread.
    Baseline : LQD=109 → 80bps spreads.
    """
    lqd = g.get("lqd", {}).get("value", 109.0)
    return max(0, round((110.0 - lqd) * 8 + 80, 0))


def _recession_prob_pct(g: dict) -> float:
    """Probabilité récession % depuis pente 10Y-3M.
    Pente positive (0.87%) → faible risque récession ~20%.
    Pente inversée (<0) → risque élevé.
    """
    slope = g.get("slope_10y3m", {}).get("value", 1.0)
    prob = 50.0 - slope * 28.0
    return max(0.0, min(95.0, round(prob, 1)))


def _energy_delta(g: dict, baseline: float = 70.0) -> float:
    """Delta prix énergie (Brent) vs baseline ($/bbl)."""
    brent = g.get("brent", {}).get("value", baseline)
    return round(brent - baseline, 2)


def _copper_score(g: dict) -> float:
    """Score cycle cuivre -5 à +5. Baseline ~$4.5/lb."""
    copper = g.get("copper", {}).get("value", 4.5)
    return round(max(-5, min(5, (copper - 4.5) * 2)), 2)


def _dxy_level(g: dict) -> float:
    return g.get("dxy", {}).get("value", 100.0)


def _vix(g: dict) -> float:
    return g.get("vix", {}).get("value", 18.0)


def _us10y_bps(g: dict) -> float:
    return round(g.get("us10y", {}).get("value", 4.5) * 100, 0)


def _us30y_bps(g: dict) -> float:
    """Approxime 30Y = 10Y + 25bps prime terme."""
    return round((_us10y_bps(g) / 100 + 0.25) * 100, 0)


def _yield_diff_usdjpy_bps(g: dict) -> float:
    """Différentiel 10Y US-Japon (JGB ≈ 1.0% depuis politique BoJ)."""
    us10y = g.get("us10y", {}).get("value", 4.5)
    jgb10y_approx = 1.0  # Policy-controlled, approximate
    return round((us10y - jgb10y_approx) * 100, 0)


def _slope_bps(g: dict) -> float:
    return round(g.get("slope_10y3m", {}).get("value", 1.0) * 100, 0)


# ── Mappings par instrument ────────────────────────────────────────────────────

def _suggest_eurusd(g: dict) -> list[dict]:
    vix = _vix(g)
    real = _us_real_rate_bps(g)
    risk = _risk_appetite_score(g)
    energy = _energy_delta(g, baseline=70.0)
    return [
        _conf("HIGH",   vix,    "m_vix",         "Niveau VIX",              "pts",    "gauge:vix",        f"VIX actuel = {vix:.1f}"),
        _conf("HIGH",   real,   "m_us_real_rate", "Taux réel US 10Y",        "bps",    "gauge:us10y+tips", f"10Y {g.get('us10y',{}).get('value',4.5):.2f}% - 2.2% breakeven ≈ {real:.0f}bps"),
        _conf("MEDIUM", risk,   "m_risk_appetite","Appétit risque mondial",  "score",  "gauge:vix+spx",    f"Score = 3 - VIX/8 + SPX200d×0.05 = {risk:.2f}"),
        _conf("MEDIUM", energy, "m_energy_price", "Prix énergie delta",      "$/bbl",  "gauge:brent",      f"Brent {g.get('brent',{}).get('value',70):.1f}$ - baseline 70$ = {energy:+.1f}"),
        _conf("LOW",    _dxy_level(g), "m_dollar_reserve", "Demande réserves USD",  "score",  "gauge:dxy",  f"DXY {_dxy_level(g):.1f} → proxy demand USD (>100 = fort)"),
    ]


def _suggest_usdjpy(g: dict) -> list[dict]:
    vix = _vix(g)
    risk = _risk_appetite_score(g)
    yd = _yield_diff_usdjpy_bps(g)
    slope = _slope_bps(g)
    return [
        _conf("HIGH",   vix,  "m_vix",          "Niveau VIX",              "pts",    "gauge:vix",        f"VIX = {vix:.1f}"),
        _conf("HIGH",   yd,   "m_yield_diff",    "Diff 10Y US-JP",          "bps",    "gauge:us10y",      f"US10Y {g.get('us10y',{}).get('value',4.5):.2f}% - JGB≈1.0% = {yd:.0f}bps"),
        _conf("MEDIUM", _us_real_rate_bps(g), "m_us_real_rate", "Taux réel US 10Y", "bps", "gauge:us10y", f"≈ {_us_real_rate_bps(g):.0f}bps"),
        _conf("MEDIUM", risk, "m_risk_appetite", "Appétit risque",          "score",  "gauge:vix",        f"Score = {risk:.2f}"),
        _conf("LOW",    slope,"m_carry_momentum","Momentum carry",           "score",  "gauge:slope",      f"Pente 10Y-3M = {slope:.0f}bps → carry actif"),
    ]


def _suggest_xauusd(g: dict) -> list[dict]:
    vix = _vix(g)
    dxy = _dxy_level(g)
    real = _us_real_rate_bps(g)
    return [
        _conf("HIGH",   vix,   "m_vix",          "Niveau VIX",              "pts",    "gauge:vix",        f"VIX = {vix:.1f}"),
        _conf("HIGH",   dxy,   "m_dxy",           "DXY (indice dollar)",     "pts",    "gauge:dxy",        f"DXY = {dxy:.1f}"),
        _conf("HIGH",   real,  "m_us_real_rate",  "Taux réel US 10Y",        "bps",    "gauge:us10y+tips", f"≈ {real:.0f}bps (10Y - 2.2% breakeven)"),
        _conf("MEDIUM", _energy_delta(g, 70), "m_fiscal_risk", "Risque fiscal US", "score", "gauge:dxy", f"DXY < 100 = doutes USD → score {round(max(0, (100-dxy)/5), 1)}"),
        _conf("LOW",    _copper_score(g), "m_india_china", "Demande physique Asie", "score", "gauge:copper", f"Cuivre {g.get('copper',{}).get('value',4.5):.2f}$/lb → proxy Asie"),
    ]


def _suggest_sp500(g: dict) -> list[dict]:
    vix   = _vix(g)
    real  = _us_real_rate_bps(g)
    ig    = _ig_spread_bps(g)
    risk  = _risk_appetite_score(g)
    spx200d = g.get("spx_vs_200d", {}).get("value", 0)
    rec_prob = _recession_prob_pct(g)
    return [
        _conf("HIGH",   vix,     "m_vix",          "Niveau VIX",              "pts",    "gauge:vix",        f"VIX = {vix:.1f}"),
        _conf("HIGH",   real,    "m_real_rate",     "Taux réel US 10Y",        "bps",    "gauge:us10y+tips", f"≈ {real:.0f}bps"),
        _conf("HIGH",   ig,      "m_ig_spread",     "Spread IG (LQD proxy)",   "bps",    "gauge:lqd",        f"LQD {g.get('lqd',{}).get('value',109):.2f} → spread ≈ {ig:.0f}bps"),
        _conf("MEDIUM", risk,    "m_fin_cond",      "Conditions financières",  "score",  "gauge:vix+lqd",    f"Score = {risk:.2f}"),
        _conf("MEDIUM", rec_prob,"m_gdp",            "Croissance PIB US",       "%",      "gauge:slope",      f"Pente 10Y-3M = {_slope_bps(g):.0f}bps → récession {rec_prob:.0f}% (inversé → PIB)"),
    ]


def _suggest_tlt(g: dict) -> list[dict]:
    us10y  = _us10y_bps(g)
    us30y  = _us30y_bps(g)
    vix    = _vix(g)
    slope  = _slope_bps(g)
    rec    = _recession_prob_pct(g)
    tp     = round(slope * 0.6, 0)  # term premium proxy
    return [
        _conf("HIGH",   us10y, "m_us_10y",          "Rendement UST 10Y",       "bps",    "gauge:us10y",   f"UST 10Y = {us10y/100:.3f}%"),
        _conf("HIGH",   us30y, "m_us_30y",           "Rendement UST 30Y",       "bps",    "gauge:us10y",   f"≈ 10Y + 25bps = {us30y/100:.3f}%"),
        _conf("HIGH",   vix,   "m_vix",              "Niveau VIX",              "pts",    "gauge:vix",     f"VIX = {vix:.1f}"),
        _conf("MEDIUM", tp,    "m_term_prem",         "Prime de terme",          "bps",    "gauge:slope",   f"Pente 10Y-3M {slope:.0f}bps × 0.6 ≈ {tp:.0f}bps"),
        _conf("MEDIUM", rec,   "m_recession_prob",    "Prob. récession 12m",     "%",      "gauge:slope",   f"Pente {slope:.0f}bps → prob ≈ {rec:.0f}%"),
        _conf("LOW",    round(_ig_spread_bps(g)/10, 1), "m_foreign_demand",
              "Demande étrangère",     "Mds$",   "gauge:lqd",     f"LQD sain → proxy demand bonds = {round(_ig_spread_bps(g)/10,1)}"),
    ]


def _suggest_gbpusd(g: dict) -> list[dict]:
    vix  = _vix(g)
    risk = _risk_appetite_score(g)
    real = _us_real_rate_bps(g)
    return [
        _conf("HIGH",   vix,  "m_vix",          "Niveau VIX",              "pts",   "gauge:vix",   f"VIX = {vix:.1f}"),
        _conf("MEDIUM", risk, "m_risk_appetite", "Appétit risque",          "score", "gauge:vix",   f"GBP devise cyclique, risk-on = {risk:.2f}"),
        _conf("LOW",    real, "m_fed_path",      "Anticipation Fed 12m",    "bps",   "gauge:us10y", f"Proxy Fed path depuis taux réels ≈ {real:.0f}bps"),
    ]


def _suggest_eem(g: dict) -> list[dict]:
    dxy  = _dxy_level(g)
    vix  = _vix(g)
    real = _us_real_rate_bps(g)
    risk = _risk_appetite_score(g)
    em_spread = round(_ig_spread_bps(g) * 1.8, 0)  # EM spreads ≈ 1.8× IG
    cop = _copper_score(g)
    return [
        _conf("HIGH",   dxy,       "m_dxy_inv",       "Dollar DXY",              "pts",   "gauge:dxy",   f"DXY = {dxy:.1f}"),
        _conf("HIGH",   vix,       "m_vix",            "Niveau VIX",             "pts",   "gauge:vix",   f"VIX = {vix:.1f}"),
        _conf("HIGH",   real,      "m_us_real_rate",   "Taux réel US 10Y",        "bps",   "gauge:us10y", f"≈ {real:.0f}bps"),
        _conf("MEDIUM", risk,      "m_risk_appetite",  "Appétit risque",          "score", "gauge:vix",   f"Score = {risk:.2f}"),
        _conf("MEDIUM", em_spread, "m_em_spread",      "Spread souverain EM",     "bps",   "gauge:lqd",   f"LQD proxy × 1.8 ≈ {em_spread:.0f}bps"),
        _conf("MEDIUM", cop,       "m_commodity_index","Indice commodités",        "score", "gauge:copper",f"Cuivre {g.get('copper',{}).get('value',4.5):.2f} → score {cop:.2f}"),
        _conf("LOW",    round(cop * 0.5, 2), "m_china_pmi", "PMI manuf. Chine", "pts",  "gauge:copper",f"Proxy cuivre → PMI-like = {round(50+cop*0.5,1)}"),
    ]


def _suggest_qqq(g: dict) -> list[dict]:
    vix  = _vix(g)
    real = _us_real_rate_bps(g)
    risk = _risk_appetite_score(g)
    spx_vs200 = g.get("spx_vs_200d", {}).get("value", 0)
    return [
        _conf("HIGH",   vix,   "m_vix",           "Niveau VIX",              "pts",   "gauge:vix",   f"VIX = {vix:.1f} (beta élevé QQQ)"),
        _conf("HIGH",   real,  "m_real_rate",      "Taux réel US 10Y (duration)","bps","gauge:us10y", f"≈ {real:.0f}bps → taux d'actualisation tech"),
        _conf("MEDIUM", risk,  "m_retail_options", "Flux options retail",     "score", "gauge:spx",   f"Risk appetite {risk:.2f} → proxy momentum options"),
        _conf("LOW",    round(spx_vs200 * 0.8, 2), "m_tech_pe",
              "Multiple PE tech (NTM)", "x", "gauge:spx_vs_200d", f"SPX vs 200d = {spx_vs200:.1f}% → PE trend"),
    ]


# ── Registry ───────────────────────────────────────────────────────────────────

_SUGGEST_FN = {
    "EURUSD": _suggest_eurusd,
    "USDJPY": _suggest_usdjpy,
    "XAUUSD": _suggest_xauusd,
    "SP500":  _suggest_sp500,
    "TLT":    _suggest_tlt,
    "GBPUSD": _suggest_gbpusd,
    "EEM":    _suggest_eem,
    "QQQ":    _suggest_qqq,
}


# ── Public API ─────────────────────────────────────────────────────────────────

def get_latest_gauges(conn, at_date: Optional[str] = None) -> tuple[dict, str]:
    """Retourne (gauges_dict, snapshot_date) depuis macro_regime_history.
    Si at_date fourni, retourne le snapshot le plus proche <= at_date."""
    if at_date:
        row = conn.execute(
            "SELECT gauges_summary_json, timestamp FROM macro_regime_history "
            "WHERE timestamp <= ? ORDER BY timestamp DESC LIMIT 1",
            (at_date,)
        ).fetchone()
    else:
        row = conn.execute(
            "SELECT gauges_summary_json, timestamp FROM macro_regime_history "
            "ORDER BY timestamp DESC LIMIT 1"
        ).fetchone()

    if row:
        r = dict(row)
        gauges = json.loads(r.get("gauges_summary_json") or "{}")
        date   = str(r.get("timestamp", ""))[:10]
        if gauges:
            return gauges, date

    # Fallback : macro_gauge_snapshots
    if at_date:
        row2 = conn.execute(
            "SELECT gauges_json, snapshot_date FROM macro_gauge_snapshots "
            "WHERE snapshot_date <= ? ORDER BY snapshot_date DESC LIMIT 1",
            (at_date,)
        ).fetchone()
    else:
        row2 = conn.execute(
            "SELECT gauges_json, snapshot_date FROM macro_gauge_snapshots "
            "ORDER BY snapshot_date DESC LIMIT 1"
        ).fetchone()
    if row2:
        return json.loads(row2["gauges_json"] or "{}"), str(row2["snapshot_date"])

    return {}, ""


def suggest_from_gauges(instrument: str, gauges: dict) -> list[dict]:
    """
    Retourne les suggestions de valeurs pour les nœuds manuels d'un instrument
    depuis les gauges de marché actuels.
    """
    fn = _SUGGEST_FN.get(instrument.upper())
    if not fn or not gauges:
        return []
    suggestions = fn(gauges)
    # Filtre les NaN / Inf
    return [
        s for s in suggestions
        if math.isfinite(s.get("value", 0))
    ]
