"""
Pydantic models for deterministic math verification.

These models define the structured data exchanged between:
1. The LLM math extractor (produces MathExtraction)
2. The SymPy verification engine (produces MathVerificationResult)
3. The evaluator prompt formatter (consumes MathVerificationResult)
"""

from typing import Dict, List, Optional

from pydantic import BaseModel, Field


class MathExtraction(BaseModel):
    """
    Structured math data extracted from educational content by the LLM.

    The math extractor LLM parses natural-language educational content and
    outputs this structured representation that SymPy can verify
    deterministically.
    """

    has_math: bool = Field(
        description="Whether the content contains verifiable mathematical claims"
    )
    domain: str = Field(
        default="other",
        description=(
            "Math domain: 'arithmetic', 'algebra', 'geometry', 'statistics', "
            "'number_theory', 'measurement', 'fractions', 'other'"
        ),
    )
    expression: Optional[str] = Field(
        default=None,
        description=(
            "SymPy-parseable expression to evaluate. "
            "Examples: '578 + 246', 'Rational(3,4) + Rational(1,3)', '2**5'"
        ),
    )
    expected_answer: Optional[str] = Field(
        default=None,
        description=(
            "The labeled correct answer in SymPy-parseable format. "
            "Examples: '824', 'Rational(13,12)', '32'"
        ),
    )
    all_options: Optional[List[str]] = Field(
        default=None,
        description=(
            "All MCQ options in SymPy-parseable format. "
            "Example: ['Rational(7,12)', 'Rational(1,2)', 'Rational(5,12)', 'Rational(13,12)']"
        ),
    )
    expected_answers: Optional[List[str]] = Field(
        default=None,
        description=(
            "For multi-solution equations (e.g. quadratics): list of ALL expected "
            "solutions in SymPy format. Example: ['3', 'Rational(1,2)']"
        ),
    )
    equation: Optional[str] = Field(
        default=None,
        description=(
            "For single-equation solving problems: the equation in SymPy Eq() form. "
            "Example: 'Eq(2*x + 3, 11)'"
        ),
    )
    equations: Optional[List[str]] = Field(
        default=None,
        description=(
            "For systems of equations: list of equations in SymPy Eq() form. "
            "Example: ['Eq(3*x + 2*y, 16)', 'Eq(x - y, 1)']"
        ),
    )
    variable: Optional[str] = Field(
        default=None,
        description="Variable to solve for in single-equation problems. Example: 'x'",
    )
    variables: Optional[List[str]] = Field(
        default=None,
        description=(
            "For systems of equations: list of variable names. "
            "Example: ['x', 'y']"
        ),
    )
    data_set: Optional[List[float]] = Field(
        default=None,
        description="Data values for statistics problems. Example: [2, 4, 6, 8, 10]",
    )
    shape_data: Optional[str] = Field(
        default=None,
        description=(
            "Geometry shape data as a JSON string. Examples: "
            '\'{"type": "rectangle", "length": 8, "width": 5}\', '
            '\'{"type": "triangle", "base": 6, "height": 4}\', '
            '\'{"type": "circle", "radius": 7}\', '
            '\'{"type": "rectangular_prism", "length": 4, "width": 3, "height": 5}\''
        ),
    )
    units: Optional[str] = Field(
        default=None,
        description=(
            "Unit conversion data as a JSON string. "
            'Example: \'{"value": 3, "from_unit": "feet", "to_unit": "inches"}\''
        ),
    )
    pi_approximation: Optional[str] = Field(
        default=None,
        description=(
            "If the problem specifies a pi approximation, provide it in SymPy format. "
            "Use 'Rational(314, 100)' for pi approx 3.14, 'Rational(22, 7)' for pi = 22/7. "
            "null if the problem uses exact pi or does not involve pi."
        ),
    )
    operation: Optional[str] = Field(
        default=None,
        description=(
            "The mathematical operation: 'evaluate', 'solve', 'simplify', "
            "'compare', 'convert', 'classify', 'find_area', 'find_volume', "
            "'find_perimeter', 'find_mean', 'find_median', 'find_mode', "
            "'find_range', 'find_gcd', 'find_lcm', 'is_prime', 'factor', "
            "'find_slope', 'find_distance', 'find_probability'"
        ),
    )

    class Config:
        extra = "forbid"


class OptionAnalysis(BaseModel):
    """Analysis of a single MCQ option."""

    option: str = Field(description="The option value (SymPy expression string)")
    is_correct: bool = Field(description="Whether this option is mathematically correct")
    computed_note: str = Field(
        default="",
        description="Explanation of why this option is correct or incorrect",
    )

    class Config:
        extra = "forbid"


class MathVerificationResult(BaseModel):
    """
    Deterministic verification output from the SymPy engine.

    If SymPy can verify the content, the result is authoritative.
    If it cannot, we return empty context and let the LLM handle it.
    No ambiguous middle ground.
    """

    is_correct: Optional[bool] = Field(
        default=None,
        description="Whether the labeled answer is correct. None if unable to verify.",
    )
    computed_answer: str = Field(
        description="The deterministically computed correct answer"
    )
    expected_answer: str = Field(
        description="What the content claimed as the correct answer"
    )
    all_options_analysis: Optional[List[OptionAnalysis]] = Field(
        default=None,
        description="Per-option analysis for MCQ questions",
    )
    verification_steps: List[str] = Field(
        default_factory=list,
        description="Step-by-step verification for transparency",
    )
    details: str = Field(
        default="",
        description="Human-readable summary of the verification",
    )

    class Config:
        extra = "forbid"
