"""Product Regulatory Navigator engine — missing-facts only.

`navigate(profile)` reports which canonical fields are still unknown and the
prioritized questions to fill them. `apply_answers(profile, answers)` returns the
updated profile. It NEVER decides what applies — that is the Scope Engine (step 3).
Pure field-presence checking; no scope-engine import, no regulation evaluation.
"""

from __future__ import annotations

from typing import Any, Dict, List, Type

from pydantic import BaseModel, Field

from compliance.profile.canonical import (
    CanonicalLifecyclePhase,
    CanonicalProductRegulatoryProfile,
    EconomicOperatorRole,
    ProductComponent,
)

from .questions import QUESTION_CATALOG, NavigatorQuestion, QuestionPriority

_ENUM_FIELDS: Dict[str, Type[Any]] = {
    "economic_operator_role": EconomicOperatorRole,
    "lifecycle_phase": CanonicalLifecyclePhase,
}


class CompletenessSummary(BaseModel):
    total_relevant: int
    answered: int
    missing: int
    missing_by_priority: Dict[str, int] = Field(default_factory=dict)
    ready_for_scope: bool  # True once no P0 fact is missing
    note: str = ""


class NavigatorResult(BaseModel):
    missing_facts: List[str] = Field(default_factory=list)  # canonical target fields
    suggested_questions: List[NavigatorQuestion] = Field(default_factory=list)
    completeness_summary: CompletenessSummary


def _value(profile: CanonicalProductRegulatoryProfile, dotted: str) -> Any:
    if "." in dotted:
        head, tail = dotted.split(".", 1)
        return getattr(getattr(profile, head), tail, None)
    return getattr(profile, dotted, None)


def _is_unknown(profile: CanonicalProductRegulatoryProfile, q: NavigatorQuestion) -> bool:
    value = _value(profile, q.target_field)
    if value is None:
        return True
    if isinstance(value, list) and not value:
        return True
    return False


def navigate(profile: CanonicalProductRegulatoryProfile) -> NavigatorResult:
    missing = [q for q in QUESTION_CATALOG if _is_unknown(profile, q)]
    missing.sort(key=lambda q: q.order())

    by_priority: Dict[str, int] = {}
    for q in missing:
        by_priority[q.priority.value] = by_priority.get(q.priority.value, 0) + 1
    ready = QuestionPriority.P0.value not in by_priority

    total = len(QUESTION_CATALOG)
    summary = CompletenessSummary(
        total_relevant=total,
        answered=total - len(missing),
        missing=len(missing),
        missing_by_priority=by_priority,
        ready_for_scope=ready,
        note=(
            "%d von %d Fakten vorhanden; %d offen. Scope-Engine startklar: %s."
            % (total - len(missing), total, len(missing), "ja" if ready else "nein (P0 fehlt)")
        ),
    )
    return NavigatorResult(
        missing_facts=[q.target_field for q in missing],
        suggested_questions=missing,
        completeness_summary=summary,
    )


def _coerce(q: NavigatorQuestion, value: Any) -> Any:
    if q.target_field in _ENUM_FIELDS:
        return _ENUM_FIELDS[q.target_field](value)
    if q.target_field == "components":
        return [c if isinstance(c, ProductComponent) else ProductComponent(**c) for c in (value or [])]
    if q.answer_type.value in {"country_list", "multiselect"}:
        return list(value or [])
    if q.answer_type.value == "bool":
        return bool(value)
    return value


def apply_answers(
    profile: CanonicalProductRegulatoryProfile, answers: Dict[str, Any]
) -> CanonicalProductRegulatoryProfile:
    updated = profile.model_copy(deep=True)
    by_id = {q.question_id: q for q in QUESTION_CATALOG}
    for question_id, raw in answers.items():
        q = by_id.get(question_id)
        if q is None or raw is None:
            continue
        value = _coerce(q, raw)
        if "." in q.target_field:
            head, tail = q.target_field.split(".", 1)
            setattr(getattr(updated, head), tail, value)
        else:
            setattr(updated, q.target_field, value)
    return updated
