"""Regulatory Map renderer (step 4) — pure composition, no new logic.

It explains the engine's state, it does not extend it: every statement comes
from `resolve_product_scope` (scope verdict) or `derive_obligations` (registry-
linked obligations + overlaps). No legal decisions here; obligations are shown
ONLY where a registry id is linkable (registry_anchor); the executive summary
carries counts but NO percentage.
"""

from __future__ import annotations

from typing import Dict, List

from compliance.navigator.engine import navigate
from compliance.product_scope.orchestrator import resolve_product_scope
from compliance.product_scope.schemas import RegulatoryScopeResult, ScopeStatus
from compliance.profile.canonical import CanonicalProductRegulatoryProfile
from compliance.profile.to_reasoning import to_reasoning_profile
from compliance.reasoning.obligation_engine import derive_obligations

from .schemas import (
    ApplicableRegulationView,
    ExcludedRegulationView,
    ObligationRef,
    OverlapView,
    RegulatoryMap,
    UncertainRegulationView,
)

_DOMAIN_BY_REG = {
    "CRA": "cyber",
    "MaschinenVO": "machine_safety",
    "RED": "radio",
    "DataAct": "data",
    "EMV": "emv",
    "NIS2": None,
}


def _product_summary(c: CanonicalProductRegulatoryProfile) -> str:
    bits: List[str] = [c.name or "Produkt"]
    if c.product_type:
        bits.append("(%s)" % c.product_type.value)
    sig: List[str] = []
    if c.is_machine:
        sig.append("Maschine")
    if c.has_remote_access or c.connected_to_internet or "cloud" in c.technologies:
        sig.append("vernetzt")
    if c.has_embedded_software:
        sig.append("Firmware")
    if c.economic_operator_role:
        sig.append("Rolle: %s" % c.economic_operator_role.value)
    if c.markets:
        sig.append("Märkte: %s" % ", ".join(c.markets))
    if sig:
        bits.append("— " + "; ".join(sig))
    return " ".join(bits)


def render_regulatory_map(profile: CanonicalProductRegulatoryProfile) -> RegulatoryMap:
    scope_resp = resolve_product_scope(profile)
    summary = _product_summary(profile)

    if scope_resp.status == ScopeStatus.NEEDS_FACTS:
        return RegulatoryMap(
            scope_resolved=False,
            product_summary=summary,
            executive_summary=(
                "Regulatorischer Scope noch nicht bestimmbar — zuerst Mindestfakten klären: "
                + "; ".join(scope_resp.missing_facts[:6])
                + "."
            ),
        )

    scope = scope_resp.regulatory_scope
    assert scope is not None
    obligations = derive_obligations(to_reasoning_profile(profile))
    nav_questions = navigate(profile).suggested_questions

    linked_ids = {o.obligation_id for o in obligations.applicable_obligations if o.registry_anchor}
    by_reg: Dict[str, List[ObligationRef]] = {}
    shared_ev: Dict[str, List[str]] = {}
    for o in obligations.applicable_obligations:
        if not o.registry_anchor:
            continue
        by_reg.setdefault(o.source_regulation, []).append(
            ObligationRef(
                obligation_id=o.obligation_id,
                title=o.title,
                legal_basis_refs=o.legal_basis_refs,
                authority_level=o.authority_level,
            )
        )
        for ev in o.required_evidence:
            shared_ev.setdefault(ev, []).append(o.obligation_id)

    applicable_views = []
    for r in scope.applicable_regulations:
        obs = by_reg.get(r.regulation_id, [])
        applicable_views.append(
            ApplicableRegulationView(
                regulation_id=r.regulation_id,
                name=r.name,
                why_applicable=r.explanation,
                triggered_by=r.trigger_facts,
                obligations=obs,
                obligations_note="" if obs else "Pflichten für dieses Regelwerk sind noch nicht registry-verlinkt.",
                confidence=r.confidence,
            )
        )

    uncertain_views = []
    for u in scope.uncertain_regulations:
        domain = _DOMAIN_BY_REG.get(u.regulation_id)
        qrefs = [q.question_id for q in nav_questions if domain and domain in q.regulatory_domains_unblocked]
        uncertain_views.append(
            UncertainRegulationView(
                regulation_id=u.regulation_id, name=u.name, missing_facts=u.missing_facts, question_refs=qrefs
            )
        )

    overlap_views = []
    for ov in obligations.overlaps:
        members = [m for m in ov.obligations if m in linked_ids]
        if len(members) >= 2:
            overlap_views.append(
                OverlapView(overlap_group_id=ov.overlap_group_id, shared_obligations=members, explanation=ov.explanation)
            )

    trigger_facts: List[str] = []
    for v in applicable_views:
        for t in v.triggered_by:
            if t not in trigger_facts:
                trigger_facts.append(t)

    return RegulatoryMap(
        scope_resolved=True,
        product_summary=summary,
        trigger_facts=trigger_facts,
        applicable_regulations=applicable_views,
        uncertain_regulations=uncertain_views,
        excluded_regulations=[
            ExcludedRegulationView(regulation_id=e.regulation_id, name=e.name, exclusion_reason=e.reason)
            for e in scope.excluded_regulations
        ],
        unsupported_domains=scope.unsupported_domains,
        overlaps=overlap_views,
        shared_evidence={ev: ids for ev, ids in shared_ev.items() if len(ids) > 1},
        executive_summary=_executive_summary(summary, applicable_views, uncertain_views, scope, len(linked_ids)),
    )


def _executive_summary(
    summary: str,
    applicable: List[ApplicableRegulationView],
    uncertain: List[UncertainRegulationView],
    scope: RegulatoryScopeResult,
    n_obligations: int,
) -> str:
    appl = ", ".join(v.regulation_id for v in applicable) or "—"
    unc = ", ".join(v.regulation_id for v in uncertain) or "keine"
    exc = ", ".join(e.regulation_id for e in scope.excluded_regulations) or "keine"
    uns = ", ".join(d.domain for d in scope.unsupported_domains) or "keine"
    return (
        "Für %s gelten nach derzeitigem Stand wahrscheinlich: %s. Unsicher (fehlende Fakten): %s. "
        "Ausgeschlossen: %s. Nicht abgedeckt (Regelkorpus fehlt): %s. Ermittelt: %d registry-verlinkte "
        "Pflichten. Es wurden keine weiteren Regelwerke im aktuellen Korpus identifiziert."
        % (summary, appl, unc, exc, uns, n_obligations)
    )
