"""RCI delta engine — assess a RegulatoryChange against a ComplianceBaseline.

Answers "what changes relative to my existing Map?" deterministically, working
ONLY against the stored baseline (no re-evaluation of scope, no new legal
assessment outside the map). Per-obligation classification -> ObligationDelta;
aggregate -> ChangeImpactSummary.
"""

from __future__ import annotations

from typing import List, Tuple

from compliance.reasoning.enums import Confidence

from .schemas import (
    ChangeAssessment,
    ChangeImpactSummary,
    ChangeType,
    ComplianceBaseline,
    DeltaType,
    ObligationDelta,
    RegulatoryChange,
)

_ACTION = {DeltaType.NEW, DeltaType.CHANGED, DeltaType.NEEDS_REVIEW}


def _classify(
    in_base: bool, has_ev: bool, change_type: ChangeType, rel_app: bool, rel_unc: bool
) -> Tuple[DeltaType, str, Confidence]:
    if not (rel_app or rel_unc):
        return DeltaType.NOT_APPLICABLE, "Die Änderung betrifft kein Regelwerk Ihrer Map.", Confidence.HIGH
    if rel_unc and not rel_app:
        return (
            DeltaType.NEEDS_REVIEW,
            "Betrifft ein für Ihr Produkt noch UNSICHERES Regelwerk — erst Anwendbarkeit klären.",
            Confidence.LOW,
        )
    if change_type == ChangeType.REPEAL:
        if in_base:
            return DeltaType.REMOVED, "Regelwerk/Pflicht aufgehoben — entfällt für Ihr Produkt.", Confidence.HIGH
        return DeltaType.NOT_APPLICABLE, "Aufhebung betrifft keine Ihrer bestehenden Pflichten.", Confidence.HIGH
    if not in_base:
        return DeltaType.NEW, "Neue Pflicht durch die Änderung — bisher nicht in Ihrer Map.", Confidence.MEDIUM
    if change_type == ChangeType.GUIDANCE_UPDATE:
        if has_ev:
            return (
                DeltaType.ALREADY_COVERED,
                "Bestehende Pflicht mit vorhandenen Nachweisen — Leitlinien-Update vermutlich abgedeckt.",
                Confidence.MEDIUM,
            )
        return DeltaType.NEEDS_REVIEW, "Bestehende Pflicht ohne Nachweis — Leitlinien-Update prüfen.", Confidence.MEDIUM
    return DeltaType.CHANGED, "Bestehende Pflicht inhaltlich geändert — Umsetzung und Nachweis prüfen.", Confidence.MEDIUM


def assess_change(baseline: ComplianceBaseline, change: RegulatoryChange) -> ChangeAssessment:
    snap = baseline.regulatory_map_snapshot
    app_regs = {v.regulation_id for v in snap.applicable_regulations}
    unc_regs = {v.regulation_id for v in snap.uncertain_regulations}
    base_obs = set(baseline.applicable_obligations)

    affected = set(change.affected_regulations)
    rel_app = bool(affected & app_regs)
    rel_unc = bool(affected & unc_regs)
    affects_product = rel_app or rel_unc

    deltas: List[ObligationDelta] = []
    for ob in change.affected_obligations:
        present = baseline.evidence_refs.get(ob, [])
        required = baseline.obligation_evidence_required.get(ob, [])
        dt, reason, conf = _classify(ob in base_obs, bool(present), change.change_type, rel_app, rel_unc)
        missing = [e for e in required if e not in present] if dt in _ACTION else []
        deltas.append(
            ObligationDelta(
                obligation_id=ob,
                delta_type=dt,
                reason=reason,
                affected_evidence=list(present),
                missing_evidence=missing,
                confidence=conf,
            )
        )

    return ChangeAssessment(
        change_id=change.change_id,
        affects_product=affects_product,
        deltas=deltas,
        summary=_summary(deltas, [d.domain for d in snap.unsupported_domains]),
    )


def _ids(deltas: List[ObligationDelta], *types: DeltaType) -> List[str]:
    wanted = set(types)
    return [d.obligation_id for d in deltas if d.delta_type in wanted]


def _summary(deltas: List[ObligationDelta], unsupported: List[str]) -> ChangeImpactSummary:
    n_new = len(_ids(deltas, DeltaType.NEW))
    n_changed = len(_ids(deltas, DeltaType.CHANGED))
    n_removed = len(_ids(deltas, DeltaType.REMOVED))
    n_covered = len(_ids(deltas, DeltaType.ALREADY_COVERED))
    n_review = len(_ids(deltas, DeltaType.NEEDS_REVIEW, DeltaType.CHANGED))
    n_na = len(_ids(deltas, DeltaType.NOT_APPLICABLE))
    return ChangeImpactSummary(
        what_changed=(
            "%d neu, %d geändert, %d entfällt, %d bereits abgedeckt, %d zu prüfen, %d nicht relevant."
            % (n_new, n_changed, n_removed, n_covered, n_review, n_na)
        ),
        what_matters_for_this_product=_ids(deltas, *_ACTION),
        already_covered=_ids(deltas, DeltaType.ALREADY_COVERED),
        needs_review=_ids(deltas, DeltaType.NEEDS_REVIEW, DeltaType.CHANGED),
        not_relevant=_ids(deltas, DeltaType.NOT_APPLICABLE),
        unsupported_domains=unsupported,
    )
