"""Anti-fuite PDF : un masque FORCÉ (override utilisateur / blacklist force-mask)
ne doit JAMAIS être retiré de l'audit par un toggle de catégorie.

Contexte (Plan 1b, P1-2/T1) :
- `_apply_overrides` masque le TEXTE inline ET ajoute un `PiiHit` dont le `kind`
  est contrôlé par l'utilisateur (`name` de l'override). Cet appel est
  inconditionnel (pas gaté par `disabled_kinds`).
- `_filter_audit_by_disabled` retire ensuite de l'audit les hits dont la
  catégorie est désactivée, AVANT la gravure PDF.
- BUG : si un utilisateur nomme un override avec une catégorie toggleable
  (ex. `name="NOM"`) et désactive cette catégorie, le texte reste masqué mais
  le hit est retiré de l'audit → la gravure PDF laisse la valeur EN CLAIR.

Correctif attendu : marquer les hits forcés (`forced=True`) et les exempter du
filtre catégorie. Un terme explicitement forcé est TOUJOURS gravé.
"""
import re

import anonymizer_core_refactored_onnx as core


def test_forced_override_hit_survives_category_filter():
    """Un override nommé "NOM" produit un hit FORCÉ qui survit au filtre {"NOM"}."""
    cfg = {
        "regex_overrides": [
            {"pattern": r"\bDupont\b", "placeholder": "[NOM]", "name": "NOM"},
        ],
    }
    audit: list = []
    line = "Patient Dupont vu ce jour."

    masked = core._apply_overrides(line, audit, 0, cfg)

    # Le texte est bien masqué (comportement inline inchangé).
    assert "Dupont" not in masked
    assert "[NOM]" in masked

    # Un hit a été produit, de catégorie NOM, et marqué forcé.
    assert len(audit) == 1
    forced_hit = audit[0]
    assert forced_hit.kind == "NOM"
    assert core._category_of(forced_hit.kind) == "NOM"
    assert getattr(forced_hit, "forced", False) is True

    # Cœur du correctif : avec NOM désactivé, le hit FORCÉ reste dans l'audit
    # (donc serait gravé dans le PDF) → pas de fuite.
    filtered = core._filter_audit_by_disabled(list(audit), {"NOM"})
    assert forced_hit in filtered, "le hit forcé a été retiré → fuite PDF"


def test_genuine_nom_hit_still_dropped_by_filter():
    """Le correctif ne sur-exempte pas : un vrai hit NOM (non forcé) est bien retiré."""
    genuine = core.PiiHit(0, "NOM", "Martin", "[NOM]")
    # Par défaut un PiiHit n'est PAS forcé.
    assert getattr(genuine, "forced", False) is False

    filtered = core._filter_audit_by_disabled([genuine], {"NOM"})
    assert genuine not in filtered, "un hit NOM non forcé doit être retiré quand NOM est désactivé"


def test_forced_blacklist_terms_marked_forced():
    """Les force_mask_terms / force_mask_regex sont aussi marqués forcés."""
    cfg = {
        "blacklist": {
            "force_mask_terms": ["CHUXX"],
            "force_mask_regex": [r"SIGLE-\d+"],
        },
    }
    audit: list = []
    line = "Etablissement CHUXX, code SIGLE-42."

    core._apply_overrides(line, audit, 0, cfg)

    assert len(audit) == 2
    for h in audit:
        assert getattr(h, "forced", False) is True, f"{h.kind} non marqué forcé"
