"""Report Parity Engine — Pydantic contracts and state machine types."""
from __future__ import annotations

import uuid
from enum import Enum
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


def _new_id(prefix: str = "rp") -> str:
    return f"{prefix}_{uuid.uuid4().hex[:12]}"


# ---------------------------------------------------------------------------
# Report specification
# ---------------------------------------------------------------------------

class ReportLineItem(BaseModel):
    """Single line item from a report."""
    line_id: str = Field(default_factory=lambda: _new_id("li"))
    label: str = ""
    measure_expression: str = ""
    source_measures: List[str] = Field(default_factory=list)
    filters: List[Dict[str, str]] = Field(default_factory=list)
    expected_values: Dict[str, float] = Field(default_factory=dict)
    group: str = ""


class ReportSpec(BaseModel):
    """Structured representation of an existing report."""
    spec_id: str = Field(default_factory=lambda: _new_id("spec"))
    report_name: str = ""
    source_system: str = ""
    grain_columns: List[str] = Field(default_factory=list)
    temporal_column: str = ""
    temporal_grain: str = "monthly"
    line_items: List[ReportLineItem] = Field(default_factory=list)
    filters: List[Dict[str, str]] = Field(default_factory=list)
    groups: List[str] = Field(default_factory=list)
    metadata: Dict[str, Any] = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Validation scope / period / slice
# ---------------------------------------------------------------------------

class ValidationScope(str, Enum):
    LINE_ITEM = "line_item"
    LINE_GROUP = "line_group"
    FULL_REPORT = "full_report"


class ValidationPeriod(str, Enum):
    MONTH = "month"
    QUARTER = "quarter"
    YEAR = "year"


class ValidationSlice(BaseModel):
    """A specific test slice (scope x period)."""
    scope: ValidationScope = ValidationScope.LINE_ITEM
    period: ValidationPeriod = ValidationPeriod.MONTH
    line_item_ids: List[str] = Field(default_factory=list)
    period_value: str = ""


# ---------------------------------------------------------------------------
# Parity test results
# ---------------------------------------------------------------------------

class ParityTestResult(BaseModel):
    """Result of a single parity test."""
    test_id: str = Field(default_factory=lambda: _new_id("pt"))
    slice: ValidationSlice = Field(default_factory=ValidationSlice)
    line_item_id: str = ""
    expected_value: float = 0.0
    actual_value: float = 0.0
    difference: float = 0.0
    difference_pct: float = 0.0
    status: str = "pending"
    tolerance_pct: float = 0.01
    dw_query: str = ""
    timestamp: str = ""


# ---------------------------------------------------------------------------
# Discrepancy classification
# ---------------------------------------------------------------------------

class DiscrepancyType(str, Enum):
    GRAIN = "grain"
    FILTER = "filter"
    HIERARCHY = "hierarchy"
    JOIN = "join"
    SIGNAGE = "signage"
    ROUNDING = "rounding"
    MISSING_DATA = "missing_data"
    EXTRA_DATA = "extra_data"
    FORMULA = "formula"


class Discrepancy(BaseModel):
    """Classified mismatch between report and DW."""
    discrepancy_id: str = Field(default_factory=lambda: _new_id("disc"))
    test_result: ParityTestResult = Field(default_factory=ParityTestResult)
    discrepancy_type: DiscrepancyType = DiscrepancyType.FORMULA
    confidence: float = Field(ge=0.0, le=1.0, default=0.5)
    root_cause_detail: str = ""
    suggested_fix: str = ""
    fix_applied: bool = False


# ---------------------------------------------------------------------------
# State machine
# ---------------------------------------------------------------------------

class GateStatus(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    PASSED = "passed"
    FAILED = "failed"
    FIXED = "fixed"
    BLOCKED = "blocked"


class ParityGate(BaseModel):
    """A single gate in the progressive validation state machine."""
    gate_id: str = Field(default_factory=lambda: _new_id("gate"))
    scope: ValidationScope = ValidationScope.LINE_ITEM
    period: ValidationPeriod = ValidationPeriod.MONTH
    status: GateStatus = GateStatus.PENDING
    predecessor_gate_id: str = ""
    test_results: List[ParityTestResult] = Field(default_factory=list)
    discrepancies: List[Discrepancy] = Field(default_factory=list)
    fix_attempts: int = 0
    max_fix_attempts: int = 3
    passed_at: str = ""
    failed_at: str = ""


class ProgressiveValidationState(BaseModel):
    """Full state of the progressive validation state machine."""
    state_id: str = Field(default_factory=lambda: _new_id("pvs"))
    spec: ReportSpec = Field(default_factory=ReportSpec)
    gates: List[ParityGate] = Field(default_factory=list)
    current_gate_index: int = 0
    overall_status: str = "pending"
    certificate: Optional[Dict[str, Any]] = None
    improvement_suggestions: List[Dict[str, Any]] = Field(default_factory=list)


# ---------------------------------------------------------------------------
# State machine transitions
# ---------------------------------------------------------------------------

GATE_TRANSITIONS = {
    GateStatus.PENDING: [GateStatus.RUNNING, GateStatus.BLOCKED],
    GateStatus.RUNNING: [GateStatus.PASSED, GateStatus.FAILED],
    GateStatus.PASSED: [],
    GateStatus.FAILED: [GateStatus.FIXED],
    GateStatus.FIXED: [GateStatus.RUNNING],
    GateStatus.BLOCKED: [GateStatus.PENDING],
}

GATE_SEQUENCE = [
    (ValidationScope.LINE_ITEM, ValidationPeriod.MONTH),
    (ValidationScope.LINE_GROUP, ValidationPeriod.MONTH),
    (ValidationScope.FULL_REPORT, ValidationPeriod.MONTH),
    (ValidationScope.FULL_REPORT, ValidationPeriod.QUARTER),
    (ValidationScope.FULL_REPORT, ValidationPeriod.YEAR),
]
