"""
Evidence analysis for constraint checking.

This module provides dual confidence evidence analysis that separates
positive evidence, negative evidence, and uncertainty.
"""

import re
from dataclasses import dataclass
from typing import Dict, List

from langchain_core.language_models import BaseChatModel
from loguru import logger

from ..constraints.base_constraint import Constraint


@dataclass
class ConstraintEvidence:
    """Evidence for a constraint with dual confidence scores."""

    positive_confidence: float  # How sure we are the constraint IS satisfied
    negative_confidence: (
        float  # How sure we are the constraint is NOT satisfied
    )
    uncertainty: float  # How uncertain we are (neither positive nor negative)
    evidence_text: str
    source: str


class EvidenceAnalyzer:
    """
    Analyzes evidence using dual confidence scoring.

    This approach separates:
    - Positive confidence: Evidence that constraint IS satisfied
    - Negative confidence: Evidence that constraint is NOT satisfied
    - Uncertainty: Lack of clear evidence either way
    """

    def __init__(self, model: BaseChatModel):
        """Initialize the evidence analyzer."""
        self.model = model

    def analyze_evidence_dual_confidence(
        self, evidence: Dict, constraint: Constraint
    ) -> ConstraintEvidence:
        """Analyze evidence to extract dual confidence scores."""
        text = evidence.get("text", "")

        # Use LLM to analyze evidence with dual confidence
        prompt = f"""
Analyze this evidence for the constraint "{constraint.value}" (type: {constraint.type.value}).

Evidence:
{text[:1000]}

Provide three confidence scores (0-1):
1. POSITIVE_CONFIDENCE: How confident are you that this constraint IS satisfied?
2. NEGATIVE_CONFIDENCE: How confident are you that this constraint is NOT satisfied?
3. UNCERTAINTY: How uncertain are you (lack of clear evidence)?

The three scores should approximately sum to 1.0.

Format:
POSITIVE: [score]
NEGATIVE: [score]
UNCERTAINTY: [score]
"""

        try:
            response = self.model.invoke(prompt).content

            # Extract scores
            positive = self._extract_score(response, "POSITIVE")
            negative = self._extract_score(response, "NEGATIVE")
            uncertainty = self._extract_score(response, "UNCERTAINTY")

            # Normalize if needed
            total = positive + negative + uncertainty
            if total > 0:
                positive /= total
                negative /= total
                uncertainty /= total
            else:
                # Default to high uncertainty
                uncertainty = 0.8
                positive = 0.1
                negative = 0.1

            return ConstraintEvidence(
                positive_confidence=positive,
                negative_confidence=negative,
                uncertainty=uncertainty,
                evidence_text=text[:500],
                source=evidence.get("source", "search"),
            )

        except Exception:
            logger.exception("Error analyzing evidence")
            # Default to high uncertainty
            return ConstraintEvidence(
                positive_confidence=0.1,
                negative_confidence=0.1,
                uncertainty=0.8,
                evidence_text=text[:500],
                source=evidence.get("source", "search"),
            )

    def _extract_score(self, text: str, label: str) -> float:
        """Extract confidence score from LLM response."""
        pattern = rf"{label}:\s*\[?(\d*\.?\d+)\]?"
        match = re.search(pattern, text, re.IGNORECASE)
        if match:
            try:
                return float(match.group(1))
            except (ValueError, TypeError):
                pass
        return 0.1  # Default low score

    def evaluate_evidence_list(
        self,
        evidence_list: List[Dict],
        constraint: Constraint,
        uncertainty_penalty: float = 0.2,
        negative_weight: float = 0.5,
    ) -> float:
        """
        Evaluate a list of evidence using dual confidence scoring.

        Args:
            evidence_list: List of evidence dictionaries
            constraint: The constraint being evaluated
            uncertainty_penalty: Penalty for uncertainty
            negative_weight: Weight for negative evidence

        Returns:
            float: Overall score between 0.0 and 1.0
        """
        if not evidence_list:
            # No evidence means high uncertainty
            return 0.5 - uncertainty_penalty

        # Convert evidence to dual confidence format
        constraint_evidence = []
        for evidence in evidence_list:
            dual_evidence = self.analyze_evidence_dual_confidence(
                evidence, constraint
            )
            constraint_evidence.append(dual_evidence)

        # Calculate overall score
        total_positive = sum(e.positive_confidence for e in constraint_evidence)
        total_negative = sum(e.negative_confidence for e in constraint_evidence)
        total_uncertainty = sum(e.uncertainty for e in constraint_evidence)

        # Normalize
        evidence_count = len(constraint_evidence)
        avg_positive = total_positive / evidence_count
        avg_negative = total_negative / evidence_count
        avg_uncertainty = total_uncertainty / evidence_count

        # Calculate final score
        # High positive + low negative = high score
        # Low positive + high negative = low score
        # High uncertainty = penalty
        score = (
            avg_positive
            - (avg_negative * negative_weight)
            - (avg_uncertainty * uncertainty_penalty)
        )

        # Clamp to [0, 1]
        return max(0.0, min(1.0, score))
