"""Pydantic domain objects for the Regulatory Reasoning Engine.

Trigger facts that drive scope are tri-state (`Optional[bool] = None`): `None`
means "fact unknown" and produces an *uncertain* verdict plus a concrete
missing-fact prompt — never silent false security (spec §6.3).
"""

from __future__ import annotations

from datetime import date
from typing import Dict, List, Optional

from pydantic import BaseModel, Field

from .enums import (
    ApplicabilityStatus,
    AuthorityLevel,
    ClaimCoverage,
    Confidence,
    InterpretationVerdict,
    ManufacturerRole,
    MarketModel,
    OverlapType,
    ProductLifecyclePhase,
)


# ---------------------------------------------------------------------------
# Input
# ---------------------------------------------------------------------------
class ProductProfile(BaseModel):
    """The customer's product / system. Tri-state booleans => unknown facts."""

    product_name: str
    product_profile_id: Optional[str] = None
    manufacturer_role: Optional[ManufacturerRole] = None
    product_type: List[str] = Field(default_factory=list)

    has_software: Optional[bool] = None
    has_embedded_software: Optional[bool] = None
    has_remote_access: Optional[bool] = None
    has_cloud_connection: Optional[bool] = None
    has_ai_functionality: Optional[bool] = None
    has_radio_module: Optional[bool] = None
    has_safety_function: Optional[bool] = None
    generates_usage_data: Optional[bool] = None

    is_machine: Optional[bool] = None
    is_component: Optional[bool] = None
    is_spare_part: Optional[bool] = None

    placed_on_market_after: Optional[date] = None
    intended_use: Optional[str] = None
    eu_market: Optional[bool] = None
    b2b_or_b2c: Optional[MarketModel] = None
    lifecycle_phase: Optional[ProductLifecyclePhase] = None

    # Organisation context — only needed for NIS2 (not a product fact).
    company_size: Optional[str] = None
    sector: Optional[str] = None
    is_essential_or_important_entity: Optional[bool] = None


# ---------------------------------------------------------------------------
# Scope
# ---------------------------------------------------------------------------
class ApplicableRegulation(BaseModel):
    regulation_id: str
    name: str
    applicability_status: ApplicabilityStatus
    trigger_facts: List[str] = Field(default_factory=list)
    legal_basis_refs: List[str] = Field(default_factory=list)
    confidence: Confidence
    explanation: str


class ExcludedRegulation(BaseModel):
    regulation_id: str
    name: str
    reason: str


class UncertainRegulation(BaseModel):
    regulation_id: str
    name: str
    missing_facts: List[str] = Field(default_factory=list)
    explanation: str


class RegulatoryScope(BaseModel):
    product_profile_id: Optional[str] = None
    applicable_regulations: List[ApplicableRegulation] = Field(default_factory=list)
    excluded_regulations: List[ExcludedRegulation] = Field(default_factory=list)
    uncertain_regulations: List[UncertainRegulation] = Field(default_factory=list)
    missing_facts: List[str] = Field(default_factory=list)
    confidence: Confidence = Confidence.MEDIUM
    reasoning_summary: str = ""


# ---------------------------------------------------------------------------
# Obligations
# ---------------------------------------------------------------------------
class ApplicableObligation(BaseModel):
    obligation_id: str
    title: str
    source_regulation: str
    legal_basis_refs: List[str] = Field(default_factory=list)
    obligation_text: str
    authority_level: AuthorityLevel
    applies_because: List[str] = Field(default_factory=list)
    applies_to_role: List[str] = Field(default_factory=list)
    lifecycle_phase: List[str] = Field(default_factory=list)
    overlap_group_id: Optional[str] = None
    required_evidence: List[str] = Field(default_factory=list)
    confidence: Confidence
    # True only when obligation_id is owned by the Legal-KG registry (CRA P1).
    registry_anchor: bool = False
    # Machine/Data-Act obligations the registry has not canonicalised yet.
    proposed: bool = False


class ObligationOverlap(BaseModel):
    overlap_group_id: str
    obligations: List[str] = Field(default_factory=list)
    overlap_type: OverlapType
    canonical_obligation_id: str
    explanation: str


# ---------------------------------------------------------------------------
# Customer claims & assessments
# ---------------------------------------------------------------------------
class CustomerImplementationClaim(BaseModel):
    claim_id: str
    raw_statement: str
    normalized_claim: str = ""
    claimed_capability: List[str] = Field(default_factory=list)
    related_topics: List[str] = Field(default_factory=list)
    qualifiers: List[str] = Field(default_factory=list)
    evidence_refs: List[str] = Field(default_factory=list)


class ClaimObligationMapping(BaseModel):
    """One row of Welt-1 reasoning: how a customer claim relates to an obligation.

    Layers (spec / architect): claim -> interpretation (on the claim object) ->
    *potential* obligation coverage (`claim_coverage`) -> evidence required.
    Carries NO compliance verdict.
    """

    claim_id: str
    obligation_id: str
    claim_coverage: ClaimCoverage
    missing_elements: List[str] = Field(default_factory=list)
    required_evidence: List[str] = Field(default_factory=list)
    explanation: str
    confidence: Confidence


class InterpretationAssessment(BaseModel):
    interpretation_id: str
    raw_interpretation: str
    affected_regulations: List[str] = Field(default_factory=list)
    affected_obligations: List[str] = Field(default_factory=list)
    assessment: InterpretationVerdict
    risks: List[str] = Field(default_factory=list)
    corrected_interpretation: str = ""
    legal_basis_refs: List[str] = Field(default_factory=list)
    explanation: str
    confidence: Confidence


# ---------------------------------------------------------------------------
# API request / response envelopes
# ---------------------------------------------------------------------------
class ScopeRequest(BaseModel):
    product_profile: ProductProfile


class ScopeResponse(BaseModel):
    regulatory_scope: RegulatoryScope
    missing_facts: List[str] = Field(default_factory=list)
    confidence: Confidence


class ObligationsRequest(BaseModel):
    product_profile: ProductProfile
    regulatory_scope: Optional[RegulatoryScope] = None


class ObligationsResponse(BaseModel):
    applicable_obligations: List[ApplicableObligation] = Field(default_factory=list)
    overlaps: List[ObligationOverlap] = Field(default_factory=list)
    excluded_obligations: List[str] = Field(default_factory=list)
    evidence_for_multiple: Dict[str, List[str]] = Field(default_factory=dict)


class ImplementationReasoningRequest(BaseModel):
    product_profile: ProductProfile
    customer_claim: str


class ImplementationReasoningResponse(BaseModel):
    claim: CustomerImplementationClaim
    mappings: List[ClaimObligationMapping] = Field(default_factory=list)
    missing_evidence: List[str] = Field(default_factory=list)
    summary: str = ""
    # Makes the Welt-1 boundary explicit: this is advisory claim-mapping, not a
    # conformity verdict (that is ComplianceStatus in the Execution Graph).
    disclaimer: str = ""


class InterpretationRequest(BaseModel):
    product_profile: Optional[ProductProfile] = None
    customer_interpretation: str


class InterpretationResponse(BaseModel):
    assessment: InterpretationVerdict
    affected_regulations: List[str] = Field(default_factory=list)
    affected_obligations: List[str] = Field(default_factory=list)
    corrected_interpretation: str = ""
    risks: List[str] = Field(default_factory=list)
    legal_basis_refs: List[str] = Field(default_factory=list)
    explanation: str = ""
    confidence: Confidence = Confidence.MEDIUM
