"""
core/ai.py — KI-Marktanalyse via OpenAI ChatGPT
=================================================
Nutzt `gpt-4o-mini-search-preview` (oder das größere `gpt-4o-search-preview`)
für eine Crude-Oil-Marktanalyse (WTI + Brent) mit Web-Suche.

Parsed eine strukturierte Antwort:
  SENTIMENT: bullish|bearish|neutral
  CONFIDENCE: 0-100
  SUMMARY: <2-3 Sätze>
  DRIVERS: <Treiber1> | <Treiber2> | <Treiber3>

Loggt jede Analyse in die History-DB (falls injiziert).
"""

from __future__ import annotations
import threading
import time

from core.logger import get_logger

log_ai   = get_logger("ai")
log_hist = get_logger("hist")


class OpenAIAnalyzer:
    """
    Ruft die OpenAI Chat-Completions API auf.

    Web-Search ist bei *-search-preview-Modellen automatisch aktiv,
    konfigurierbar über `search_context` (low|medium|high).
    """

    PROMPT = (
        "Du bist ein professioneller Rohstoff-Marktanalyst. Suche im Web nach den "
        "wichtigsten Crude-Oil-News der letzten 24 Stunden — sowohl für "
        "WTI (US Crude) als auch Brent (OPEC, US-Lagerbestände/EIA, "
        "Geopolitik Naher Osten, Förderdaten, Nachfrage-Indikatoren, "
        "Pipelines, Raffinerien).\n\n"
        "Antworte AUSSCHLIESSLICH in genau diesem Format (deutsche Sprache):\n"
        "SENTIMENT: bullish|bearish|neutral\n"
        "CONFIDENCE: <0-100>\n"
        "SUMMARY: <2-3 Sätze, prägnant, nur preisbewegende Faktoren>\n"
        "DRIVERS: <Treiber1> | <Treiber2> | <Treiber3>\n\n"
        "Sei knapp und konkret. Keine Disclaimer, keine Einleitung."
    )

    # Preis pro Mio Token (USD) – grobe Schätzung für Cost-Anzeige
    PRICES = {
        "gpt-4o-mini-search-preview": (0.15,  0.60, 25.0),
        "gpt-4o-search-preview":      (2.50, 10.00, 30.0),
        "_default":                   (0.15,  0.60, 25.0),
    }

    def __init__(self, api_key: str, model: str, search_context: str = "low"):
        self.api_key        = api_key.strip()
        self.model          = model.strip() or "gpt-4o-mini-search-preview"
        self.search_context = search_context.strip().lower() or "low"
        self.sentiment      = None
        self.confidence     = None
        self.summary        = ""
        self.drivers        = []
        self.last_update    = None
        self.last_cost      = None
        self.error          = None
        self.busy           = False
        self._lock          = threading.Lock()
        # History-Logger wird vom main() injiziert
        self.history = None

    def is_configured(self) -> bool:
        return bool(self.api_key) and self.api_key.startswith(("sk-", "sk-proj-"))

    def _estimate_cost(self, in_tok: int, out_tok: int) -> float:
        in_p, out_p, search_p = self.PRICES.get(self.model, self.PRICES["_default"])
        token_cost  = (in_tok * in_p + out_tok * out_p) / 1_000_000
        search_cost = search_p / 1000.0
        return token_cost + search_cost

    def analyze(self):
        if not self.is_configured():
            with self._lock:
                self.error = "Kein API-Key (config.ini)"
            return
        try:
            from openai import OpenAI
        except ImportError:
            with self._lock:
                self.error = "pip install openai"
            return

        with self._lock:
            if self.busy:
                return
            self.busy  = True
            self.error = None

        try:
            client = OpenAI(api_key=self.api_key)
            kwargs = {
                "model":      self.model,
                "messages":   [{"role": "user", "content": self.PROMPT}],
            }
            if "search-preview" in self.model:
                kwargs["web_search_options"] = {
                    "search_context_size": self.search_context,
                }
            else:
                kwargs["max_tokens"] = 700

            res  = client.chat.completions.create(**kwargs)
            text = (res.choices[0].message.content or "").strip()

            # Strukturierte Antwort parsen
            sentiment  = "neutral"
            confidence = 50
            summary    = text[:300]
            drivers    = []
            for raw in text.split("\n"):
                line = raw.strip()
                low  = line.lower()
                if low.startswith("sentiment:"):
                    v = low.split(":", 1)[1].strip()
                    sentiment = ("bullish" if "bull" in v
                                 else "bearish" if "bear" in v else "neutral")
                elif low.startswith("confidence:"):
                    digits = "".join(c for c in line.split(":", 1)[1] if c.isdigit())[:3]
                    if digits:
                        confidence = max(0, min(100, int(digits)))
                elif low.startswith("summary:"):
                    summary = line.split(":", 1)[1].strip()
                elif low.startswith("drivers:"):
                    parts = line.split(":", 1)[1]
                    drivers = [d.strip() for d in parts.split("|") if d.strip()][:4]

            # Token-Verbrauch & Kosten
            usage = getattr(res, "usage", None)
            cost_est = None
            if usage:
                in_tok  = getattr(usage, "prompt_tokens",     0) or 0
                out_tok = getattr(usage, "completion_tokens", 0) or 0
                cost_est = self._estimate_cost(in_tok, out_tok)

            with self._lock:
                self.sentiment   = sentiment
                self.confidence  = confidence
                self.summary     = summary
                self.drivers     = drivers
                self.last_update = time.time()
                self.last_cost   = cost_est
                self.error       = None

            log_ai.info(f"{sentiment.upper()} ({confidence}%) — {summary[:80]}")
            if cost_est:
                log_ai.info(f"Geschätzte Kosten: ${cost_est:.4f}")

            if self.history:
                try:
                    self.history.log_ai(
                        sentiment    = sentiment,
                        confidence   = confidence,
                        summary      = summary,
                        drivers      = drivers,
                        cost_estimate= cost_est,
                        model        = self.model,
                    )
                except Exception as e:
                    log_hist.error(f"log_ai: {e}")
        except Exception as e:
            err = str(e)
            with self._lock:
                self.error = err[:120]
            log_ai.error(f"Fehler: {err}")
        finally:
            with self._lock:
                self.busy = False

    def snapshot(self):
        with self._lock:
            return {
                "sentiment":   self.sentiment,
                "confidence":  self.confidence,
                "summary":     self.summary,
                "drivers":     list(self.drivers),
                "last_update": self.last_update,
                "last_cost":   self.last_cost,
                "error":       self.error,
                "busy":        self.busy,
                "configured":  self.is_configured(),
                "model":       self.model,
            }


class GeminiAnalyzer:
    """
    Ruft die Google Gemini API auf (REST, via requests).
    Nutzt Google Search Grounding für aktuelle Web-Daten.
    """

    PROMPT = (
        "Du bist ein professioneller Rohstoff-Marktanalyst. Suche im Web nach den "
        "wichtigsten Crude-Oil-News der letzten 24 Stunden — sowohl für "
        "WTI (US Crude) als auch Brent (OPEC, US-Lagerbestände/EIA, "
        "Geopolitik Naher Osten, Förderdaten, Nachfrage-Indikatoren, "
        "Pipelines, Raffinerien).\n\n"
        "Antworte AUSSCHLIESSLICH in genau diesem Format (deutsche Sprache):\n"
        "SENTIMENT: bullish|bearish|neutral\n"
        "CONFIDENCE: <0-100>\n"
        "SUMMARY: <2-3 Sätze, prägnant, nur preisbewegende Faktoren>\n"
        "DRIVERS: <Treiber1> | <Treiber2> | <Treiber3>\n\n"
        "Sei knapp und konkret. Keine Disclaimer, keine Einleitung."
    )

    _API_BASE = "https://generativelanguage.googleapis.com/v1beta/models"

    def __init__(self, api_key: str, model: str = "gemini-2.0-flash"):
        self.api_key        = api_key.strip()
        self.model          = model.strip() or "gemini-2.0-flash"
        self.sentiment      = None
        self.confidence     = None
        self.summary        = ""
        self.drivers        = []
        self.last_update    = None
        self.error          = None
        self.busy           = False
        self._lock          = threading.Lock()
        self.history        = None

    def is_configured(self) -> bool:
        return bool(self.api_key) and self.api_key.startswith("AIza")

    def analyze(self):
        if not self.is_configured():
            with self._lock:
                self.error = "Kein Gemini API-Key (config.ini)"
            return
        try:
            import requests as _req
        except ImportError:
            with self._lock:
                self.error = "pip install requests"
            return

        with self._lock:
            if self.busy:
                return
            self.busy  = True
            self.error = None

        try:
            url = f"{self._API_BASE}/{self.model}:generateContent"
            payload = {
                "contents": [{"role": "user", "parts": [{"text": self.PROMPT}]}],
                "tools": [{"google_search": {}}],
                "generationConfig": {
                    "temperature":    0.3,
                    "maxOutputTokens": 600,
                },
            }
            # Key im Header statt als URL-Parameter — sonst landet er bei
            # jedem HTTP-Fehler im Klartext in der Log-Fehlermeldung (URL)
            resp = _req.post(
                url,
                headers={"x-goog-api-key": self.api_key},
                json=payload,
                timeout=60,
            )
            resp.raise_for_status()
            result = resp.json()

            text = ""
            try:
                text = result["candidates"][0]["content"]["parts"][0]["text"].strip()
            except (KeyError, IndexError):
                text = str(result)[:300]

            sentiment  = "neutral"
            confidence = 50
            summary    = text[:300]
            drivers    = []
            for raw in text.split("\n"):
                line = raw.strip()
                low  = line.lower()
                if low.startswith("sentiment:"):
                    v = low.split(":", 1)[1].strip()
                    sentiment = ("bullish" if "bull" in v
                                 else "bearish" if "bear" in v else "neutral")
                elif low.startswith("confidence:"):
                    digits = "".join(c for c in line.split(":", 1)[1] if c.isdigit())[:3]
                    if digits:
                        confidence = max(0, min(100, int(digits)))
                elif low.startswith("summary:"):
                    summary = line.split(":", 1)[1].strip()
                elif low.startswith("drivers:"):
                    parts = line.split(":", 1)[1]
                    drivers = [d.strip() for d in parts.split("|") if d.strip()][:4]

            with self._lock:
                self.sentiment   = sentiment
                self.confidence  = confidence
                self.summary     = summary
                self.drivers     = drivers
                self.last_update = time.time()
                self.error       = None

            log_ai.info(f"[Gemini] {sentiment.upper()} ({confidence}%) — {summary[:80]}")

            if self.history:
                try:
                    self.history.log_ai(
                        sentiment    = sentiment,
                        confidence   = confidence,
                        summary      = summary,
                        drivers      = drivers,
                        cost_estimate= None,
                        model        = self.model,
                    )
                except Exception as e:
                    log_hist.error(f"[Gemini] log_ai: {e}")

        except Exception as e:
            err = str(e)
            with self._lock:
                self.error = err[:120]
            log_ai.error(f"[Gemini] Fehler: {err}")
        finally:
            with self._lock:
                self.busy = False

    def snapshot(self):
        with self._lock:
            return {
                "sentiment":   self.sentiment,
                "confidence":  self.confidence,
                "summary":     self.summary,
                "drivers":     list(self.drivers),
                "last_update": self.last_update,
                "last_cost":   None,
                "error":       self.error,
                "busy":        self.busy,
                "configured":  self.is_configured(),
                "model":       self.model,
            }
