from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional, List, Dict, Any
import json
from services.ai_analyzer import (
    analyze_speech, evaluate_pattern, suggest_pattern_from_context,
    rank_trade_ideas, analyze_news_item, get_client, score_patterns_with_context,
    suggest_patterns_from_market_context, ai_score_news_batch,
    DEFAULT_ANALYSIS_TEMPLATE, _chat,
)
from services.data_fetcher import fetch_geo_news, get_all_quotes
from services.geo_analyzer import compute_geo_risk_score, match_patterns
from services.database import get_config, get_custom_patterns, get_analysis_config, save_pattern_scores, get_pattern_scores, get_score_deltas, compute_pattern_similarity, log_geo_alert, log_trade_entries

router = APIRouter(prefix="/api/ai", tags=["ai"])


def require_ai():
    key = get_config("openai_api_key") or ""
    if not key:
        raise HTTPException(400, "Clé OpenAI non configurée — aller dans Config")
    import os
    os.environ["OPENAI_API_KEY"] = key


class SpeechRequest(BaseModel):
    text: str
    speaker: Optional[str] = ""


class PatternEvalRequest(BaseModel):
    pattern: Dict[str, Any]


class PatternSuggestRequest(BaseModel):
    context: str


class NewsAnalyzeRequest(BaseModel):
    title: str
    summary: str


class ScorePatternsRequest(BaseModel):
    top_n: Optional[int] = None           # override config default
    category_filter: Optional[str] = None # "all"|"energy"|"metals"|"agriculture"|"forex"|"indices"|"equities"
    template: Optional[str] = None        # override config template


@router.get("/status")
def ai_status():
    key = get_config("openai_api_key") or ""
    return {
        "enabled": bool(key),
        "key_configured": bool(key),
        "model": "gpt-4o",
        "fast_model": "gpt-4o-mini",
    }


@router.post("/analyze-speech")
def analyze_speech_endpoint(req: SpeechRequest):
    require_ai()
    return analyze_speech(req.text, req.speaker)


@router.post("/analyze-news")
def analyze_news_endpoint(req: NewsAnalyzeRequest):
    require_ai()
    return analyze_news_item(req.title, req.summary)


@router.post("/evaluate-pattern")
def evaluate_pattern_endpoint(req: PatternEvalRequest):
    require_ai()
    return evaluate_pattern(req.pattern)


@router.post("/suggest-pattern")
def suggest_pattern_endpoint(req: PatternSuggestRequest):
    require_ai()
    return suggest_pattern_from_context(req.context)


@router.get("/top-ideas")
def top_ideas():
    require_ai()
    from routers.geopolitical import _news_cache
    news = _news_cache.get("data") or fetch_geo_news()
    matches = match_patterns(news)
    geo_score = compute_geo_risk_score(news)
    ideas = rank_trade_ideas(matches, geo_score, news, {})
    return {"ideas": ideas, "count": len(ideas)}


@router.post("/score-patterns")
def score_patterns(req: ScorePatternsRequest):
    """Score all patterns with rich context (news + prices + IV) via GPT-4o."""
    require_ai()

    # Load config defaults
    cfg = get_analysis_config()
    top_n = req.top_n or cfg.get("top_n", 10)
    category_filter = req.category_filter or cfg.get("category_filter", "all")
    template = req.template or cfg.get("template") or DEFAULT_ANALYSIS_TEMPLATE

    # Gather context
    from routers.geopolitical import _news_cache
    news = _news_cache.get("data") or fetch_geo_news()

    # AI-rescore news: get accurate impact magnitudes + directional signals per asset class
    # This enables contra-signal detection in pattern scoring (e.g. peace deal → oil bearish)
    news = ai_score_news_batch(news)
    _news_cache["data"] = news  # propagate AI-enriched scores back to news feed

    geo_score = compute_geo_risk_score(news)
    quotes = get_all_quotes()

    # Macro regime context (use cached value from /api/market/macro-regime if available)
    from routers.market_data import _macro_cache
    macro_regime = _macro_cache.get("data")
    if not macro_regime:
        from services.data_fetcher import get_macro_gauges, score_macro_scenarios
        gauges = get_macro_gauges()
        macro_regime = {"gauges": gauges, "scenarios": score_macro_scenarios(gauges)}

    # All patterns from DB (builtin-seeded + custom)
    all_patterns = get_custom_patterns()

    # Score all active patterns
    scored = score_patterns_with_context(
        patterns=all_patterns,
        recent_news=news,
        quotes_by_class=quotes,
        geo_score=geo_score,
        template=template,
        top_n=len(all_patterns),
        category_filter=None,
        macro_regime=macro_regime,
    )

    # Persist scores for later retrieval
    run_id = save_pattern_scores(scored, meta={"geo_score": geo_score.get("score"), "total": len(scored)})

    # Journal de Bord — log geo alert + trade entry prices at this moment
    top_patterns_for_log = sorted(
        [{"pattern_id": sp.get("pattern_id"), "name": sp.get("geo_trigger"), "score": sp.get("score", 0)}
         for sp in scored if sp.get("score", 0) > 0],
        key=lambda x: -x["score"]
    )[:10]
    log_geo_alert(
        geo_score=int(geo_score.get("score") or 0),
        top_patterns=top_patterns_for_log,
        news_count=len(news),
        run_id=run_id,
    )
    log_trade_entries(run_id=run_id, scored_patterns=scored, quotes=quotes)

    return {
        "scored_patterns": scored,
        "count": len(scored),
        "top_n": top_n,
        "category_filter": category_filter,
        "geo_score": geo_score.get("score"),
    }


@router.get("/last-scores")
def last_scores():
    """Return last persisted AI pattern scores with inter-run score deltas."""
    data = get_pattern_scores()
    deltas = get_score_deltas()
    scored = data.get("scores", [])
    for sp in scored:
        pid = sp.get("pattern_id", "")
        sp["score_trend"] = deltas.get(pid)  # None = first run, int = change vs previous run
    return {
        "scored_patterns": scored,
        "scored_at": data.get("scored_at"),
        "meta": data.get("meta", {}),
        "count": len(scored),
    }


@router.get("/pattern-similarity")
def pattern_similarity():
    """Return pairs of patterns with high keyword overlap (Jaccard >= 0.25)."""
    patterns = get_custom_patterns()
    pairs = compute_pattern_similarity(patterns, threshold=0.25)
    return {"pairs": pairs, "count": len(pairs)}


@router.post("/suggest-new-patterns")
def suggest_new_patterns():
    """Ask GPT-4o to propose new patterns from current geo/market context (no text input needed)."""
    require_ai()
    from routers.geopolitical import _news_cache
    news = _news_cache.get("data") or fetch_geo_news()
    quotes = get_all_quotes()
    from services.data_fetcher import get_economic_calendar
    calendar = get_economic_calendar()

    # Macro regime context
    from routers.market_data import _macro_cache
    macro_regime = _macro_cache.get("data")
    if not macro_regime:
        from services.data_fetcher import get_macro_gauges, score_macro_scenarios
        gauges = get_macro_gauges()
        macro_regime = {"gauges": gauges, "scenarios": score_macro_scenarios(gauges)}

    # Geo risk score for additional context
    geo_score = compute_geo_risk_score(news)

    patterns = suggest_patterns_from_market_context(news, quotes, calendar, macro_regime=macro_regime, geo_score=geo_score)
    return {"suggested_patterns": patterns, "count": len(patterns)}


@router.get("/analysis-template")
def get_template():
    cfg = get_analysis_config()
    return {
        "template": cfg.get("template") or DEFAULT_ANALYSIS_TEMPLATE,
        "top_n": cfg.get("top_n", 10),
        "category_filter": cfg.get("category_filter", "all"),
    }


class MacroNarrationRequest(BaseModel):
    macro_regime: Dict[str, Any]


@router.post("/macro-narration")
def macro_narration(req: MacroNarrationRequest):
    """Ask GPT-4o to narrate the current macro regime for the MacroRegime page."""
    require_ai()
    sc = req.macro_regime.get("scenarios", {})
    gauges = req.macro_regime.get("gauges", {})
    dom = sc.get("dominant", "incertain")
    scores = sc.get("scores", {})
    reasons = sc.get("reasons", {})

    def gv(key: str) -> str:
        v = gauges.get(key, {}).get("value")
        return str(round(v, 3)) if v is not None else "N/A"

    def gc(key: str) -> str:
        v = gauges.get(key, {}).get("change_pct")
        return f"{v:+.2f}%" if v is not None else "N/A"

    user = f"""Tu es un stratège macro senior utilisant un framework à 3 axes : Inflation / Croissance / Liquidité.

Scénario dominant: {dom.upper()}
Scores des 8 scénarios: {json.dumps(scores, ensure_ascii=False)}
Raisons du scénario dominant: {json.dumps(reasons.get(dom, []), ensure_ascii=False)}

AXE INFLATION (énergie + taux réels):
- Brent (var J-1): {gc('brent')}
- Gaz naturel (var J-1): {gc('ng')}
- TIPS ETF (var J-1): {gc('tips')}

AXE CROISSANCE (cycle réel):
- Cuivre (var J-1): {gc('copper')} — "Dr Copper"
- S&P vs 200j MA: {gv('spx_vs_200d')}%
- Russell vs S&P (breadth): {gv('iwm_spx_ratio')} pts%
- Industriels XLI (proxy ISM, var J-1): {gc('xli')}

AXE LIQUIDITÉ / STRESS (conditions financières):
- VIX: {gv('vix')}
- Pente 10Y–3M: {gv('slope_10y3m')}% (négatif = récession probable)
- HYG spreads HY (var J-1): {gc('hyg')}
- LQD spreads IG (var J-1): {gc('lqd')}
- Dollar DXY (var J-1): {gc('dxy')}

SIGNAUX DÉRIVÉS:
- Ratio Or/Cuivre: {gv('gold_copper_ratio')} (>700 = peur, <500 = expansion)
- Or (var J-1): {gc('gold')}
- IEF Trésor (var J-1): {gc('ief')}

Donne une analyse narrative COURTE (5-7 phrases) en français pour un trader options:
1. Confirme le régime dominant et ses 2-3 signaux les plus forts
2. Identifie 1-2 compteurs en contradiction ou tension (signal ambigu)
3. Évalue si la LIQUIDITÉ confirme ou contredit le régime inflation/croissance
4. Cite les 2-3 classes d'actifs les plus favorisées/défavorisées
5. Donne 1 biais tactique concret options pour les prochains jours

Réponds UNIQUEMENT en JSON: {{"narration": "<ton texte 5-7 phrases>"}}"""

    result = _chat(
        "Tu es un stratège macro senior. Analyse concise et actionnable pour traders options. JSON uniquement.",
        user,
        model="gpt-4o",
        json_mode=True,
        max_tokens=600,
    )
    if not result:
        return {"narration": "IA non disponible — vérifier la clé OpenAI."}
    return {"narration": result.get("narration", "")}
