"""
SAGE Reasoning Verification

Provides verification checkpoints for reasoning chains to ensure
logical consistency and citation validation in safety-critical responses.
"""

import logging
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

logger = logging.getLogger(__name__)


class VerificationStatus(str, Enum):
    """Status of verification check."""

    PASSED = "passed"
    FAILED = "failed"
    WARNING = "warning"
    SKIPPED = "skipped"


class VerificationError(str, Enum):
    """Types of verification errors."""

    MISSING_CITATION = "missing_citation"
    INVALID_CITATION = "invalid_citation"
    INCONSISTENT_REASONING = "inconsistent_reasoning"
    UNSUPPORTED_CLAIM = "unsupported_claim"
    CONTRADICTORY_STEPS = "contradictory_steps"
    MISSING_ASSUMPTION = "missing_assumption"


@dataclass
class VerificationResult:
    """Result of a verification checkpoint."""

    status: VerificationStatus
    checkpoint: str
    message: str
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    details: dict[str, Any] = field(default_factory=dict)


@dataclass
class ReasoningStep:
    """A single step in a reasoning chain."""

    step_number: int
    action: str
    result: str
    citations: list[str] = field(default_factory=list)
    assumptions: list[str] = field(default_factory=list)
    confidence: float = 1.0


@dataclass
class ReasoningChain:
    """A complete reasoning chain for verification."""

    steps: list[ReasoningStep]
    conclusion: str
    confidence: float
    classification: str  # GREEN, YELLOW, RED
    citations: list[str] = field(default_factory=list)


class ReasoningVerifier:
    """
    Verifies reasoning chains for logical consistency and citation support.

    Provides checkpoints to catch reasoning errors before safety-critical
    responses are delivered.

    Example:
        verifier = ReasoningVerifier()

        chain = ReasoningChain(
            steps=[...],
            conclusion="The configuration is subcritical",
            confidence=0.95,
            classification="GREEN"
        )

        results = verifier.verify_chain(chain)
        if all(r.status == VerificationStatus.PASSED for r in results):
            # Chain is verified
            pass
    """

    def __init__(self, strict: bool = False) -> None:
        """Initialize the reasoning verifier.

        Args:
            strict: If True, treat warnings as failures
        """
        self.strict = strict
        logger.info(f"Initialized ReasoningVerifier (strict={strict})")

    def verify_chain(
        self,
        chain: ReasoningChain,
        available_sources: list[str] | None = None,
    ) -> list[VerificationResult]:
        """
        Verify a complete reasoning chain.

        Runs all verification checkpoints and returns results.

        Args:
            chain: ReasoningChain to verify
            available_sources: List of valid source document IDs

        Returns:
            List of VerificationResult for each checkpoint
        """
        results = []

        # Checkpoint 1: Citation validation
        citation_result = self._verify_citations(chain, available_sources)
        results.append(citation_result)

        # Checkpoint 2: Step consistency
        consistency_result = self._verify_step_consistency(chain)
        results.append(consistency_result)

        # Checkpoint 3: Assumption validation
        assumption_result = self._verify_assumptions(chain)
        results.append(assumption_result)

        # Checkpoint 4: Conclusion support
        conclusion_result = self._verify_conclusion_support(chain)
        results.append(conclusion_result)

        # Checkpoint 5: Confidence calibration
        confidence_result = self._verify_confidence_calibration(chain)
        results.append(confidence_result)

        return results

    def check_consistency(
        self,
        chain: ReasoningChain,
    ) -> tuple[bool, list[str]]:
        """
        Check if reasoning steps are internally consistent.

        Args:
            chain: ReasoningChain to check

        Returns:
            Tuple of (consistent, list of issues)
        """
        issues = []

        # Check for contradictory statements
        statements = []
        for step in chain.steps:
            statements.append(step.result.lower())

        # Simple contradiction detection
        contradiction_pairs = [
            ("subcritical", "supercritical"),
            ("safe", "unsafe"),
            ("below limit", "above limit"),
            ("compliant", "non-compliant"),
        ]

        for s1, s2 in contradiction_pairs:
            has_s1 = any(s1 in stmt for stmt in statements)
            has_s2 = any(s2 in stmt for stmt in statements)
            if has_s1 and has_s2:
                issues.append(f"Contradictory statements: '{s1}' and '{s2}' both present")

        # Check confidence progression
        prev_confidence = 1.0
        for step in chain.steps:
            if step.confidence > prev_confidence + 0.1:
                issues.append(
                    f"Step {step.step_number} confidence ({step.confidence:.2f}) "
                    f"unexpectedly higher than previous ({prev_confidence:.2f})"
                )
            prev_confidence = step.confidence

        return len(issues) == 0, issues

    def validate_assumptions(
        self,
        chain: ReasoningChain,
    ) -> tuple[bool, list[str]]:
        """
        Validate that all assumptions are stated explicitly.

        Args:
            chain: ReasoningChain to validate

        Returns:
            Tuple of (valid, list of issues)
        """
        issues = []

        # Collect all assumptions
        all_assumptions = []
        for step in chain.steps:
            all_assumptions.extend(step.assumptions)

        # Check for implicit assumptions in conclusions
        implicit_indicators = [
            "assuming",
            "if we assume",
            "given that",
            "it follows that",
            "therefore",
        ]

        conclusion_lower = chain.conclusion.lower()
        for indicator in implicit_indicators:
            if indicator in conclusion_lower:
                # Check if there's a corresponding explicit assumption
                found = False
                for assumption in all_assumptions:
                    if any(word in assumption.lower() for word in indicator.split()):
                        found = True
                        break
                if not found:
                    issues.append(
                        f"Implicit assumption indicator '{indicator}' in conclusion "
                        "without explicit assumption in reasoning steps"
                    )

        return len(issues) == 0, issues

    def _verify_citations(
        self,
        chain: ReasoningChain,
        available_sources: list[str] | None,
    ) -> VerificationResult:
        """Verify all citations reference valid sources."""
        errors = []
        warnings = []

        # Collect all citations from chain
        all_citations = set(chain.citations)
        for step in chain.steps:
            all_citations.update(step.citations)

        if not all_citations:
            warnings.append("No citations provided in reasoning chain")

        # Validate against available sources if provided
        if available_sources is not None:
            for citation in all_citations:
                if citation not in available_sources:
                    errors.append(f"Citation not in available sources: {citation}")

        # Check that safety-critical claims have citations
        safety_keywords = ["limit", "subcritical", "critical", "margin", "standard"]
        for step in chain.steps:
            result_lower = step.result.lower()
            has_safety_claim = any(kw in result_lower for kw in safety_keywords)
            if has_safety_claim and not step.citations:
                warnings.append(
                    f"Step {step.step_number} makes safety-critical claim without citation"
                )

        if errors:
            status = VerificationStatus.FAILED
            message = f"Citation validation failed: {len(errors)} error(s)"
        elif warnings and self.strict:
            status = VerificationStatus.FAILED
            message = f"Citation validation failed (strict mode): {len(warnings)} warning(s)"
        elif warnings:
            status = VerificationStatus.WARNING
            message = f"Citation validation passed with {len(warnings)} warning(s)"
        else:
            status = VerificationStatus.PASSED
            message = "All citations validated"

        return VerificationResult(
            status=status,
            checkpoint="citation_validation",
            message=message,
            errors=errors,
            warnings=warnings,
        )

    def _verify_step_consistency(self, chain: ReasoningChain) -> VerificationResult:
        """Verify reasoning steps are internally consistent."""
        consistent, issues = self.check_consistency(chain)

        if not consistent:
            return VerificationResult(
                status=VerificationStatus.FAILED,
                checkpoint="step_consistency",
                message=f"Reasoning steps have {len(issues)} consistency issue(s)",
                errors=issues,
            )

        return VerificationResult(
            status=VerificationStatus.PASSED,
            checkpoint="step_consistency",
            message="Reasoning steps are internally consistent",
        )

    def _verify_assumptions(self, chain: ReasoningChain) -> VerificationResult:
        """Verify all assumptions are explicitly stated."""
        valid, issues = self.validate_assumptions(chain)

        if not valid:
            return VerificationResult(
                status=VerificationStatus.WARNING,
                checkpoint="assumption_validation",
                message=f"Found {len(issues)} assumption issue(s)",
                warnings=issues,
            )

        return VerificationResult(
            status=VerificationStatus.PASSED,
            checkpoint="assumption_validation",
            message="All assumptions explicitly stated",
        )

    def _verify_conclusion_support(self, chain: ReasoningChain) -> VerificationResult:
        """Verify conclusion is supported by reasoning steps."""
        warnings = []

        if not chain.steps:
            return VerificationResult(
                status=VerificationStatus.FAILED,
                checkpoint="conclusion_support",
                message="No reasoning steps to support conclusion",
                errors=["Empty reasoning chain"],
            )

        # Check that conclusion relates to step results
        conclusion_keywords = set(chain.conclusion.lower().split())
        step_keywords: set[str] = set()
        for step in chain.steps:
            step_keywords.update(step.result.lower().split())

        # Remove common words
        common_words = {"the", "a", "an", "is", "are", "was", "were", "be", "to", "of", "and", "or"}
        conclusion_keywords -= common_words
        step_keywords -= common_words

        overlap = conclusion_keywords & step_keywords
        if len(overlap) < 2:
            warnings.append("Conclusion may not be well-supported by reasoning steps")

        if warnings:
            return VerificationResult(
                status=VerificationStatus.WARNING,
                checkpoint="conclusion_support",
                message="Conclusion support verification has warnings",
                warnings=warnings,
            )

        return VerificationResult(
            status=VerificationStatus.PASSED,
            checkpoint="conclusion_support",
            message="Conclusion is supported by reasoning steps",
        )

    def _verify_confidence_calibration(self, chain: ReasoningChain) -> VerificationResult:
        """Verify confidence level is appropriately calibrated."""
        warnings = []

        # Check overall confidence vs step confidences
        if chain.steps:
            min_step_confidence = min(s.confidence for s in chain.steps)
            if chain.confidence > min_step_confidence + 0.1:
                warnings.append(
                    f"Chain confidence ({chain.confidence:.2f}) is higher than "
                    f"minimum step confidence ({min_step_confidence:.2f})"
                )

        # Check confidence vs classification
        if chain.classification == "GREEN" and chain.confidence < 0.8:
            warnings.append(
                f"GREEN classification with low confidence ({chain.confidence:.2f})"
            )
        elif chain.classification == "RED" and chain.confidence > 0.7:
            warnings.append(
                f"RED classification with high confidence ({chain.confidence:.2f}) - "
                "consider if this warrants review"
            )

        if warnings:
            return VerificationResult(
                status=VerificationStatus.WARNING,
                checkpoint="confidence_calibration",
                message="Confidence calibration has warnings",
                warnings=warnings,
            )

        return VerificationResult(
            status=VerificationStatus.PASSED,
            checkpoint="confidence_calibration",
            message="Confidence level appropriately calibrated",
        )
