"""
Options Technical Agent — validates each logged trade from a pure options pricing perspective.

Checks performed per trade:
  1. IV Rank / IV Percentile       → is vol cheap or expensive?
  2. IV vs Historical Vol ratio     → is options premium justified?
  3. Skew (put/call)               → what is the market hedging against?
  4. Term Structure (contango/back) → front-month stress or calm?
  5. Options flow (call/put ratio)  → smart money direction?
  6. Strategy / IV fit             → is the strategy appropriate for current vol regime?

Verdict: OK | WARN | ALERT
"""
import logging
from typing import Any, Dict, List, Optional, Tuple

logger = logging.getLogger(__name__)

# ── Configurable thresholds ────────────────────────────────────────────────────
# These can be overridden by auto_cycle._apply_iv_gate() using values from
# the app_config DB table (keys: iv_gate_ivr_high, iv_gate_ivr_extreme,
# iv_gate_skew_threshold). Edit them via the Config page in the UI.

_IVR_HIGH: float = 60.0      # IVR above this → no naked long vol
_IVR_EXTREME: float = 80.0   # IVR above this → sell vol only
_SKEW_THRESH: float = 8.0    # Put skew above this → puts very expensive

# ── IV regime → strategy mapping ───────────────────────────────────────────────

IV_STRATEGY_RULES = {
    "long_vol": ["long call", "long put", "long straddle", "long strangle"],
    "short_vol": ["short call", "short put", "short strangle", "iron condor",
                  "covered call", "cash secured put", "cash-secured put"],
    "spread": ["bull call spread", "bear put spread", "bull put spread",
               "bear call spread", "call spread", "put spread"],
}

STRATEGY_REGIME = {
    # IVR < 30  → buy vol (cheap)
    "low":   {"preferred": ["Long Call", "Long Put", "Long Straddle"],
               "avoid":    ["Iron Condor", "Short Strangle"]},
    # IVR 30-60 → spreads (moderate cost)
    "mid":   {"preferred": ["Bull Call Spread", "Bear Put Spread", "Bull Put Spread", "Bear Call Spread"],
               "avoid":    ["Long Straddle", "Long Strangle"]},
    # IVR > 60  → sell vol / use spreads (expensive)
    "high":  {"preferred": ["Bull Call Spread", "Bear Put Spread", "Cash-Secured Put",
                             "Iron Condor", "Covered Call", "Short Strangle"],
               "avoid":    ["Long Call", "Long Put", "Long Straddle", "Long Strangle"]},
}


def _iv_regime(iv_rank: Optional[float]) -> str:
    if iv_rank is None:
        return "unknown"
    if iv_rank < 30:
        return "low"
    if iv_rank < 60:
        return "mid"
    return "high"


def _infer_direction(strategy: str) -> str:
    s = strategy.lower()
    if any(k in s for k in ["call", "bull", "haussier"]):
        return "bullish"
    if any(k in s for k in ["put", "bear", "baissier"]):
        return "bearish"
    if any(k in s for k in ["straddle", "strangle", "iron condor"]):
        return "neutral"
    return "bullish"


def _optimal_strategy(direction: str, iv_rank: Optional[float]) -> str:
    regime = _iv_regime(iv_rank)
    if direction == "bullish":
        return {"low": "Long Call", "mid": "Bull Call Spread", "high": "Cash-Secured Put ou Bull Put Spread"}.get(regime, "Bull Call Spread")
    elif direction == "bearish":
        return {"low": "Long Put", "mid": "Bear Put Spread", "high": "Covered Call ou Bear Call Spread"}.get(regime, "Bear Put Spread")
    else:
        return {"low": "Long Straddle", "mid": "Iron Condor (wing buy)", "high": "Iron Condor ou Short Strangle"}.get(regime, "Iron Condor")


def assess_strategy_fit(
    strategy: str,
    iv_rank: Optional[float],
    iv_current_pct: Optional[float],
    iv_min_52w: Optional[float],
    iv_max_52w: Optional[float],
    skew_pct: Optional[float],
    term_structure: Optional[str],
    flow_bias: Optional[str],
) -> Dict[str, Any]:
    """Rule-based assessment of strategy vs options pricing environment."""
    issues: List[str] = []
    positives: List[str] = []
    score = 100
    s = strategy.lower()

    is_long_vol = any(k in s for k in IV_STRATEGY_RULES["long_vol"])
    is_short_vol = any(k in s for k in IV_STRATEGY_RULES["short_vol"])
    is_spread = any(k in s for k in IV_STRATEGY_RULES["spread"])
    direction = _infer_direction(strategy)
    regime = _iv_regime(iv_rank)

    # ── Rule 1: IV Rank fit (uses configurable thresholds _IVR_HIGH / _IVR_EXTREME) ──
    ivr_high = _IVR_HIGH        # default 60, configurable via app_config
    ivr_extreme = _IVR_EXTREME  # default 80
    if iv_rank is not None:
        if is_long_vol:
            if iv_rank >= ivr_extreme:
                is_double_sided = any(k in s for k in ["straddle", "strangle"])
                issues.append(
                    f"IVR {iv_rank:.0f}% — achat de vol au pic annuel (IV crush quasi-certain). "
                    f"Min 52s={iv_min_52w:.1f}% | Max={iv_max_52w:.1f}%"
                    if iv_min_52w and iv_max_52w else
                    f"IVR {iv_rank:.0f}% — achat de vol au pic annuel, IV crush probable"
                )
                # Straddles/strangles buy BOTH sides at peak IV → double vega exposure
                score -= 60 if is_double_sided else 56
            elif iv_rank >= ivr_high:
                issues.append(f"IVR {iv_rank:.0f}% — vol chère, un spread débiteur réduirait le coût de vega de ~40-60%")
                score -= 25
            elif iv_rank <= 20:
                positives.append(f"IVR {iv_rank:.0f}% — vol bon marché, timing idéal pour acheter des options")
                score += 5
            elif iv_rank <= 35:
                positives.append(f"IVR {iv_rank:.0f}% — vol modérément bon marché, stratégie long vol pertinente")
        elif is_short_vol:
            if iv_rank <= 25:
                issues.append(f"IVR {iv_rank:.0f}% — prime collectée faible, risque/rendement défavorable pour vendeur")
                score -= 25
            elif iv_rank >= ivr_high + 5:
                positives.append(f"IVR {iv_rank:.0f}% — vol chère, timing favorable pour la vente de prime")
                score += 10
        elif is_spread:
            if iv_rank >= ivr_high - 10:
                positives.append(f"IVR {iv_rank:.0f}% — spread adapté : coût vega réduit, convient au régime de vol élevée")
            elif iv_rank <= 20:
                issues.append(f"IVR {iv_rank:.0f}% — vol bon marché, option pure plus efficace qu'un spread (gain plafonné inutilement)")
                score -= 10

    # ── Rule 2: IV vs Historical Vol ratio ──────────────────────────────────
    if iv_current_pct and iv_min_52w and iv_max_52w:
        iv_range = iv_max_52w - iv_min_52w
        if iv_range > 0:
            iv_normalized = (iv_current_pct - iv_min_52w) / iv_range * 100
            # This is essentially IVR recalculated — use for extra context
            if is_long_vol and iv_normalized >= 90:
                issues.append(
                    f"IV actuelle {iv_current_pct:.1f}% vs range 52s [{iv_min_52w:.1f}%–{iv_max_52w:.1f}%] "
                    f"— dans le top 10% du range annuel"
                )

    # ── Rule 3: Skew (uses _SKEW_THRESH) ─────────────────────────────────────
    skew_thresh = _SKEW_THRESH  # default 8
    if skew_pct is not None:
        if skew_pct > skew_thresh and "put" in s and is_long_vol:
            issues.append(f"Skew put élevé ({skew_pct:+.1f}pts) — protection déjà très chère, marché en mode hedge")
            score -= 15
        elif skew_pct > skew_thresh * 0.6:
            issues.append(f"Skew put positif ({skew_pct:+.1f}pts) — demande de protection élevée, marché anxieux")
            score -= 8
        elif skew_pct < -skew_thresh * 0.6 and "call" in s and is_long_vol:
            issues.append(f"Skew call négatif ({skew_pct:+.1f}pts) — demande de calls élevée, options call chères relativement")
            score -= 8
        elif abs(skew_pct) <= 3:
            positives.append(f"Skew neutre ({skew_pct:+.1f}pts) — pas de biais de protection excessif dans le marché")

    # ── Rule 4: Term structure ────────────────────────────────────────────────
    if term_structure:
        if term_structure == "backwardation" and is_short_vol:
            issues.append("Term structure en backwardation — vol front-month > back, vendre la vol est risqué en période de stress")
            score -= 20
        elif term_structure == "backwardation" and is_long_vol and "straddle" in s:
            positives.append("Backwardation + long straddle : vol front-month élevée, catalyseur événementiel probable")
            score += 8
        elif term_structure == "contango" and is_short_vol:
            positives.append("Contango : vol croît avec le temps, calendar spread ou vente de vol front-month avantageuse")
            score += 5
        elif term_structure == "contango" and is_long_vol:
            issues.append("Contango : vol augmente avec l'échéance, payer le temps est coûteux pour les options longues")
            score -= 5

    # ── Rule 5: Options flow ──────────────────────────────────────────────────
    if flow_bias:
        if flow_bias == "bearish" and direction == "bullish":
            issues.append("Flow options bearish (plus de puts achetés) alors que la stratégie est haussière — signal contra")
            score -= 10
        elif flow_bias == "bullish" and direction == "bearish":
            issues.append("Flow options bullish (plus de calls achetés) alors que la stratégie est baissière — signal contra")
            score -= 10
        elif flow_bias == direction:
            positives.append(f"Flow options aligné sur la direction ({flow_bias}) — confirmation par le smart money")
            score += 5

    score = max(0, min(100, score))
    verdict = "OK" if score >= 70 else "WARN" if score >= 45 else "ALERT"

    return {
        "fit_score": score,
        "verdict": verdict,
        "issues": issues,
        "positives": positives,
        "direction_inferred": direction,
        "iv_regime": regime,
        "optimal_strategy": _optimal_strategy(direction, iv_rank),
    }


# ── Main agent function ────────────────────────────────────────────────────────

def assess_logged_trades(
    scoring_run_id: str,
    scored: List[Dict],
    ai_key: str,
) -> Optional[Dict]:
    """
    For each trade logged in this cycle:
      1. Fetch IV snapshot (IVR, skew, term structure, flow)
      2. Run rule-based assessment
      3. GPT-4o narrative for all trades together
    Returns dict with per-trade assessments + global assessment.
    """
    import os
    import json as _json
    os.environ["OPENAI_API_KEY"] = ai_key

    # ── Get newly logged trades ───────────────────────────────────────────────
    try:
        from services.database import get_conn
        conn = get_conn()
        rows = conn.execute(
            """SELECT id, underlying, strategy, entry_date, score_at_entry, pattern_name, capital_invested
               FROM trade_entry_prices WHERE run_id=? ORDER BY entry_date DESC""",
            (scoring_run_id,),
        ).fetchall()
        conn.close()
        trades = [dict(r) for r in rows]
    except Exception as e:
        logger.warning(f"[OptionsTech] Failed to fetch logged trades: {e}")
        return None

    if not trades:
        logger.info("[OptionsTech] No trades logged this cycle — skipping assessment")
        return {"assessments": [], "global_assessment": "", "global_score": None}

    # ── Fetch IV snapshots per unique ticker ──────────────────────────────────
    from services.iv_engine import get_full_iv_snapshot
    iv_snapshots: Dict[str, Dict] = {}
    unique_tickers = list({t["underlying"] for t in trades if t.get("underlying")})

    for ticker in unique_tickers:
        try:
            iv_snapshots[ticker] = get_full_iv_snapshot(ticker)
            logger.debug(f"[OptionsTech] IV snapshot fetched for {ticker}")
        except Exception as e:
            logger.debug(f"[OptionsTech] IV snapshot failed for {ticker}: {e}")
            iv_snapshots[ticker] = {}

    # ── Rule-based assessment per trade ──────────────────────────────────────
    assessments: List[Dict] = []
    for t in trades:
        ticker = t.get("underlying") or ""
        strategy = t.get("strategy") or "Long Call"
        snap = iv_snapshots.get(ticker, {})

        iv_rank = snap.get("iv_rank")
        iv_current_pct = snap.get("iv_current_pct")
        iv_min_52w = snap.get("iv_min_52w_pct")
        iv_max_52w = snap.get("iv_max_52w_pct")
        skew = snap.get("skew", {})
        skew_pct = skew.get("skew_pct")
        term = snap.get("term_structure", {})
        term_structure = term.get("structure")
        flow = snap.get("options_flow", {})
        flow_bias = flow.get("flow_bias")
        call_put_ratio = flow.get("call_put_ratio")
        iv_source = snap.get("iv_source", "none")

        fit = assess_strategy_fit(
            strategy=strategy,
            iv_rank=iv_rank,
            iv_current_pct=iv_current_pct,
            iv_min_52w=iv_min_52w,
            iv_max_52w=iv_max_52w,
            skew_pct=skew_pct,
            term_structure=term_structure,
            flow_bias=flow_bias,
        )

        assessments.append({
            "trade_id": t.get("id"),
            "ticker": ticker,
            "strategy": strategy,
            "pattern_name": t.get("pattern_name", ""),
            # IV data
            "iv_rank": iv_rank,
            "iv_current_pct": iv_current_pct,
            "iv_min_52w_pct": iv_min_52w,
            "iv_max_52w_pct": iv_max_52w,
            "iv_source": iv_source,
            "skew_pct": skew_pct,
            "skew_interpretation": skew.get("interpretation"),
            "term_structure": term_structure,
            "term_iv_30d": term.get("iv_30d"),
            "term_iv_90d": term.get("iv_90d"),
            "flow_bias": flow_bias,
            "call_put_ratio": call_put_ratio,
            # Rule-based verdict
            "fit_score": fit["fit_score"],
            "verdict": fit["verdict"],
            "issues": fit["issues"],
            "positives": fit["positives"],
            "iv_regime": fit["iv_regime"],
            "direction_inferred": fit["direction_inferred"],
            "optimal_strategy": fit["optimal_strategy"],
            # GPT-4o analysis filled below
            "analysis": "",
            "when_to_enter": "",
        })

    # ── GPT-4o narrative (all trades together) ────────────────────────────────
    global_assessment = ""
    global_score = None
    try:
        from services.ai_analyzer import _chat

        trades_for_prompt = []
        for a in assessments:
            entry = {
                "ticker": a["ticker"],
                "strategy": a["strategy"],
                "pattern": a["pattern_name"],
                "iv_rank_pct": a["iv_rank"],
                "iv_current_pct": a["iv_current_pct"],
                "iv_range_52w": f"{a['iv_min_52w_pct']:.1f}%–{a['iv_max_52w_pct']:.1f}%"
                    if a["iv_min_52w_pct"] and a["iv_max_52w_pct"] else None,
                "skew_pts": a["skew_pct"],
                "skew_interp": a["skew_interpretation"],
                "term_structure": a["term_structure"],
                "iv_30d": a["term_iv_30d"],
                "iv_90d": a["term_iv_90d"],
                "flow_bias": a["flow_bias"],
                "call_put_ratio": a["call_put_ratio"],
                "rule_verdict": a["verdict"],
                "rule_issues": a["issues"],
                "rule_positives": a["positives"],
                "optimal_strategy_suggested": a["optimal_strategy"],
            }
            trades_for_prompt.append(entry)

        prompt = f"""Tu es un trader d'options senior avec 20 ans d'expérience en market making et volatilité.
Analyse les trades ci-dessous qui viennent d'être loggés dans notre système.
Pour chaque trade, fournis une analyse technique options rigoureuse en utilisant TOUS les indicateurs disponibles.

TRADES À ANALYSER:
{_json.dumps(trades_for_prompt, ensure_ascii=False, indent=2)}

Pour chaque trade, analyse:
1. Le TIMING de vol (IVR, range 52s, IV/HV implicite) — est-ce le bon moment pour cette stratégie ?
2. La STRUCTURE (skew, term structure, flow) — que dit le marché options lui-même ?
3. La STRATÉGIE choisie — est-elle optimale pour ce régime de vol ?
4. Le PRICE d'ENTRÉE OPTIMAL — quelle condition améliorerait le timing ?

Rappel règles d'or:
- IVR > 70% + achat d'option naked = payer la prime maximale = IV crush probable
- Skew put élevé = marché en mode protection, puts chers
- Backwardation = stress, ne pas vendre la vol
- Iron Condor / Short Strangle = uniquement IVR > 65%
- Long Straddle = uniquement si catalyseur + IVR < 30%

Réponds en JSON EXACT:
{{
  "trade_assessments": [
    {{
      "ticker": "<ticker>",
      "strategy": "<stratégie>",
      "technical_score": <0-100>,
      "analysis": "<3-4 phrases d'analyse technique précise: timing vol, skew, structure>",
      "when_to_enter": "<condition précise et mesurable pour un meilleur timing: ex 'Attendre IVR < 40%', 'Après event X'>"
    }}
  ],
  "global_assessment": "<2-3 phrases sur la qualité technique globale des entrées de ce cycle>",
  "global_score": <0-100, note technique globale du cycle d'entrées>
}}"""

        result = _chat(
            "Tu es un expert options/volatilité. Analyse technique précise en JSON.",
            prompt,
            model="gpt-4o",
            json_mode=True,
            max_tokens=1200,
        )

        if result:
            global_assessment = result.get("global_assessment", "")
            global_score = result.get("global_score")
            ai_assessments = {a["ticker"]: a for a in result.get("trade_assessments", [])}
            for a in assessments:
                ai = ai_assessments.get(a["ticker"], {})
                a["analysis"] = ai.get("analysis", "")
                a["when_to_enter"] = ai.get("when_to_enter", "")
                if ai.get("technical_score") is not None:
                    a["fit_score"] = int(ai["technical_score"])
                    a["verdict"] = "OK" if a["fit_score"] >= 70 else "WARN" if a["fit_score"] >= 45 else "ALERT"

            logger.info(f"[OptionsTech] GPT-4o assessment complete — {len(assessments)} trades, global_score={global_score}")

    except Exception as e:
        logger.warning(f"[OptionsTech] GPT-4o narrative failed: {e}")

    return {
        "run_id": scoring_run_id,
        "assessments": assessments,
        "global_assessment": global_assessment,
        "global_score": global_score,
        "n_alert": sum(1 for a in assessments if a["verdict"] == "ALERT"),
        "n_warn": sum(1 for a in assessments if a["verdict"] == "WARN"),
        "n_ok": sum(1 for a in assessments if a["verdict"] == "OK"),
    }


def save_assessments_to_db(assessments: List[Dict], run_id: str) -> None:
    """Persist per-trade assessments for Journal badge display."""
    import json as _json
    try:
        from services.database import get_conn
        conn = get_conn()
        for a in assessments:
            conn.execute(
                """INSERT OR REPLACE INTO options_trade_assessments
                   (run_id, trade_id, ticker, strategy, assessed_at,
                    iv_rank, iv_current_pct, skew_pct, term_structure,
                    fit_score, verdict, issues_json, optimal_strategy, analysis, when_to_enter)
                   VALUES (?, ?, ?, ?, datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    run_id,
                    a.get("trade_id"),
                    a.get("ticker"),
                    a.get("strategy"),
                    a.get("iv_rank"),
                    a.get("iv_current_pct"),
                    a.get("skew_pct"),
                    a.get("term_structure"),
                    a.get("fit_score"),
                    a.get("verdict"),
                    _json.dumps(a.get("issues", []) + (["✓ " + p for p in a.get("positives", [])]), ensure_ascii=False),
                    a.get("optimal_strategy"),
                    a.get("analysis", ""),
                    a.get("when_to_enter", ""),
                ),
            )
        conn.commit()
        conn.close()
    except Exception as e:
        logger.warning(f"[OptionsTech] DB save failed: {e}")


# ── IV-strategy injection string for AI prompts ────────────────────────────────

OPTIONS_STRATEGY_RULES = """
## ⚠️ RÈGLES STRICTES IV → STRATÉGIE (à appliquer pour chaque trade suggéré)

Le choix de stratégie doit tenir compte du COÛT de la volatilité implicite (IVR = IV Rank 52 semaines):

| IVR | Stratégie AUTORISÉE | Stratégie INTERDITE |
|-----|---------------------|---------------------|
| < 30% (vol cheap)    | Long Call, Long Put, Long Straddle | Iron Condor, Short Strangle |
| 30–60% (vol moderate) | Bull Call Spread, Bear Put Spread   | Long Straddle, Long Strangle naked |
| 60–80% (vol chère)   | Spreads débiteurs, Cash-Secured Put | Long Call naked, Long Put naked |
| > 80% (vol très chère)| Iron Condor, Short Strangle, Covered Call, Cash-Secured Put | TOUTE option long naked |

Règles supplémentaires:
- Skew put élevé (> 5 pts) → éviter Long Put (trop cher), préférer Bear Put Spread
- Term structure backwardation → ne pas vendre de vol (vendeur piégé si vol monte encore)
- Si IVR inconnu → utiliser Bull Call Spread / Bear Put Spread par défaut (neutre au coût de vol)
- Long Straddle uniquement si: IVR < 25% ET catalyseur événementiel clairement identifié
"""
