"""
Relevance Scorer für Alerts.

Nutzt das LLM Gateway um Alerts auf Relevanz zu prüfen.
Berücksichtigt das Nutzerprofil für personalisierte Filterung.
"""

import json
import logging
import re
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Optional

import httpx

from ..models.alert_item import AlertItem, AlertStatus
from ..models.relevance_profile import RelevanceProfile


logger = logging.getLogger(__name__)


class RelevanceDecision(str, Enum):
    """Relevanz-Entscheidung für einen Alert."""
    KEEP = "KEEP"      # Relevant, in Inbox anzeigen
    DROP = "DROP"      # Irrelevant, automatisch archivieren
    REVIEW = "REVIEW"  # Unsicher, Nutzer soll entscheiden


@dataclass
class ScoringResult:
    """Ergebnis des Relevanz-Scorings."""
    alert_id: str
    score: float  # 0.0 - 1.0
    decision: RelevanceDecision
    reason_codes: list = field(default_factory=list)
    summary: Optional[str] = None
    entities: dict = field(default_factory=dict)  # Extrahierte Entitäten
    model_version: str = ""
    prompt_version: str = "1.0"
    scored_at: datetime = field(default_factory=datetime.utcnow)
    error: Optional[str] = None

    def to_dict(self) -> dict:
        return {
            "alert_id": self.alert_id,
            "score": self.score,
            "decision": self.decision.value,
            "reason_codes": self.reason_codes,
            "summary": self.summary,
            "entities": self.entities,
            "model_version": self.model_version,
            "prompt_version": self.prompt_version,
            "scored_at": self.scored_at.isoformat(),
            "error": self.error,
        }


# System Prompt für Relevanz-Scoring
RELEVANCE_SYSTEM_PROMPT = """Du bist ein Relevanz-Filter für News-Alerts. Deine Aufgabe ist es, zu bewerten, ob ein Alert für den Nutzer relevant ist.

## Deine Aufgaben:
1. Analysiere den Alert-Titel und Snippet
2. Berücksichtige das Nutzerprofil (Prioritäten, Ausschlüsse, Beispiele)
3. Gib eine Relevanz-Bewertung ab

## Bewertungskriterien:
- **KEEP** (Score 0.7-1.0): Alert ist klar relevant für die Prioritäten des Nutzers
- **REVIEW** (Score 0.4-0.7): Möglicherweise relevant, Nutzer sollte entscheiden
- **DROP** (Score 0.0-0.4): Nicht relevant, kann ignoriert werden

## Ausschluss-Gründe (automatisch DROP):
- Stellenanzeigen, Werbung, Pressemitteilungen (außer hochrelevant)
- Duplicate/sehr ähnliche Meldung zu kürzlichem Alert
- Thema in Ausschlussliste des Nutzers

## Output-Format:
Du MUSST mit einem JSON-Objekt antworten (keine Markdown-Codeblöcke, nur das JSON):
{
  "score": 0.85,
  "decision": "KEEP",
  "reason_codes": ["matches_priority_inklusion", "recent_news"],
  "summary": "Kurze Zusammenfassung des Alerts (1-2 Sätze)",
  "entities": {
    "topics": ["Inklusion", "Bayern"],
    "organizations": ["Kultusministerium"],
    "date_context": "aktuell"
  }
}

Wichtig:
- score ist eine Zahl zwischen 0.0 und 1.0
- decision ist entweder "KEEP", "DROP" oder "REVIEW"
- reason_codes sind kurze, maschinenlesbare Codes
- summary ist auf Deutsch
"""


class RelevanceScorer:
    """
    Scorer für Alert-Relevanz.

    Nutzt das LLM Gateway für die Bewertung.
    """

    def __init__(
        self,
        gateway_url: str = "http://localhost:8000/llm",
        api_key: str = "",
        model: str = "breakpilot-teacher-8b",
        timeout: int = 30,
    ):
        """
        Initialisiere RelevanceScorer.

        Args:
            gateway_url: URL des LLM Gateway
            api_key: API Key für Gateway
            model: Modell für Scoring
            timeout: HTTP Timeout
        """
        self.gateway_url = gateway_url.rstrip("/")
        self.api_key = api_key
        self.model = model
        self.timeout = timeout
        self._client: Optional[httpx.AsyncClient] = None

        # Schwellenwerte
        self.keep_threshold = 0.7
        self.drop_threshold = 0.4

    async def _get_client(self) -> httpx.AsyncClient:
        """Hole oder erstelle HTTP Client."""
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(
                timeout=self.timeout,
                headers={
                    "X-API-Key": self.api_key,
                    "Content-Type": "application/json",
                },
            )
        return self._client

    async def close(self) -> None:
        """Schließe HTTP Client."""
        if self._client:
            await self._client.aclose()
            self._client = None

    def _build_user_prompt(self, alert: AlertItem) -> str:
        """Erstelle User-Prompt für einen Alert."""
        parts = [
            f"## Alert zu bewerten\n",
            f"**Thema-Label:** {alert.topic_label}",
            f"**Titel:** {alert.title}",
        ]

        if alert.snippet:
            # Snippet auf 500 Zeichen begrenzen
            snippet = alert.snippet[:500]
            if len(alert.snippet) > 500:
                snippet += "..."
            parts.append(f"**Snippet:** {snippet}")

        if alert.url:
            parts.append(f"**URL:** {alert.url}")

        if alert.published_at:
            parts.append(f"**Veröffentlicht:** {alert.published_at.strftime('%Y-%m-%d')}")

        parts.append("\nBewerte diesen Alert und antworte NUR mit dem JSON-Objekt.")

        return "\n".join(parts)

    def _build_system_prompt(self, profile: Optional[RelevanceProfile] = None) -> str:
        """Erstelle System-Prompt mit optionalem Profil."""
        system = RELEVANCE_SYSTEM_PROMPT

        if profile:
            system += "\n\n" + profile.get_prompt_context()

        return system

    def _parse_response(self, text: str, alert_id: str) -> ScoringResult:
        """Parse LLM Response in ScoringResult."""
        try:
            # Versuche JSON zu extrahieren
            # Manchmal wrapped das LLM in Markdown Code-Blocks
            json_match = re.search(r"\{[\s\S]*\}", text)
            if not json_match:
                raise ValueError("Kein JSON in Response gefunden")

            data = json.loads(json_match.group())

            score = float(data.get("score", 0.5))
            score = max(0.0, min(1.0, score))  # Clamp to 0-1

            decision_str = data.get("decision", "REVIEW").upper()
            try:
                decision = RelevanceDecision(decision_str)
            except ValueError:
                # Fallback basierend auf Score
                if score >= self.keep_threshold:
                    decision = RelevanceDecision.KEEP
                elif score <= self.drop_threshold:
                    decision = RelevanceDecision.DROP
                else:
                    decision = RelevanceDecision.REVIEW

            return ScoringResult(
                alert_id=alert_id,
                score=score,
                decision=decision,
                reason_codes=data.get("reason_codes", []),
                summary=data.get("summary"),
                entities=data.get("entities", {}),
                model_version=self.model,
            )

        except json.JSONDecodeError as e:
            logger.warning(f"JSON Parse Error für Alert {alert_id}: {e}")
            return ScoringResult(
                alert_id=alert_id,
                score=0.5,
                decision=RelevanceDecision.REVIEW,
                reason_codes=["parse_error"],
                error=f"JSON parse error: {str(e)}",
                model_version=self.model,
            )

        except Exception as e:
            logger.error(f"Unexpected error parsing response: {e}")
            return ScoringResult(
                alert_id=alert_id,
                score=0.5,
                decision=RelevanceDecision.REVIEW,
                reason_codes=["error"],
                error=str(e),
                model_version=self.model,
            )

    async def score_alert(
        self,
        alert: AlertItem,
        profile: Optional[RelevanceProfile] = None,
    ) -> ScoringResult:
        """
        Bewerte einen einzelnen Alert.

        Args:
            alert: Der zu bewertende Alert
            profile: Optional Nutzerprofil für personalisierte Bewertung

        Returns:
            ScoringResult mit Bewertung
        """
        try:
            client = await self._get_client()

            # Request Body
            payload = {
                "model": self.model,
                "messages": [
                    {"role": "system", "content": self._build_system_prompt(profile)},
                    {"role": "user", "content": self._build_user_prompt(alert)},
                ],
                "temperature": 0.3,  # Niedrig für konsistentere Ergebnisse
                "max_tokens": 500,
            }

            response = await client.post(
                f"{self.gateway_url}/v1/chat/completions",
                json=payload,
            )
            response.raise_for_status()

            data = response.json()
            content = data["choices"][0]["message"]["content"]

            result = self._parse_response(content, alert.id)

            # Update Alert
            alert.relevance_score = result.score
            alert.relevance_decision = result.decision.value
            alert.relevance_reasons = result.reason_codes
            alert.relevance_summary = result.summary
            alert.status = AlertStatus.SCORED

            return result

        except httpx.HTTPStatusError as e:
            error_msg = f"HTTP {e.response.status_code}: {e.response.text[:200]}"
            logger.error(f"Gateway Error für Alert {alert.id}: {error_msg}")
            return ScoringResult(
                alert_id=alert.id,
                score=0.5,
                decision=RelevanceDecision.REVIEW,
                reason_codes=["gateway_error"],
                error=error_msg,
                model_version=self.model,
            )

        except Exception as e:
            logger.exception(f"Scoring Error für Alert {alert.id}")
            return ScoringResult(
                alert_id=alert.id,
                score=0.5,
                decision=RelevanceDecision.REVIEW,
                reason_codes=["error"],
                error=str(e),
                model_version=self.model,
            )

    async def score_batch(
        self,
        alerts: list[AlertItem],
        profile: Optional[RelevanceProfile] = None,
        skip_scored: bool = True,
    ) -> list[ScoringResult]:
        """
        Bewerte mehrere Alerts.

        Args:
            alerts: Liste von Alerts
            profile: Nutzerprofil
            skip_scored: Bereits bewertete überspringen

        Returns:
            Liste von ScoringResults
        """
        results = []

        for alert in alerts:
            if skip_scored and alert.status == AlertStatus.SCORED:
                logger.debug(f"Alert {alert.id} bereits bewertet, überspringe")
                continue

            result = await self.score_alert(alert, profile)
            results.append(result)

            # Kurze Pause zwischen Requests um Rate Limits zu vermeiden
            # await asyncio.sleep(0.1)

        return results

    def get_stats(self, results: list[ScoringResult]) -> dict:
        """Generiere Statistiken über Scoring-Ergebnisse."""
        total = len(results)
        if total == 0:
            return {"total": 0}

        keep = sum(1 for r in results if r.decision == RelevanceDecision.KEEP)
        drop = sum(1 for r in results if r.decision == RelevanceDecision.DROP)
        review = sum(1 for r in results if r.decision == RelevanceDecision.REVIEW)
        errors = sum(1 for r in results if r.error)

        avg_score = sum(r.score for r in results) / total

        return {
            "total": total,
            "keep": keep,
            "drop": drop,
            "review": review,
            "errors": errors,
            "keep_rate": keep / total,
            "drop_rate": drop / total,
            "avg_score": avg_score,
        }


# Singleton Instance
_scorer_instance: Optional[RelevanceScorer] = None


def get_relevance_scorer(
    gateway_url: str = "http://localhost:8000/llm",
    api_key: str = "",
    model: str = "breakpilot-teacher-8b",
) -> RelevanceScorer:
    """Hole Singleton RelevanceScorer Instanz."""
    global _scorer_instance
    if _scorer_instance is None:
        _scorer_instance = RelevanceScorer(
            gateway_url=gateway_url,
            api_key=api_key,
            model=model,
        )
    return _scorer_instance
