"""Privilege and confidentiality banner detection (R-5), deterministic.

Independent of the optional classifier (R-12); always runs in the core. Each banner is
tagged by class (privilege vs confidentiality) and emitted at severity high.

Precision note: the bare word "confidential" appears constantly in ordinary prose, so a
lone "confidential" is matched only when it is ALL CAPS in the source (a banner), or
when it sits in a known banner phrase. Multi-word privilege markers match
case-insensitively.
"""
from __future__ import annotations

import re
from typing import Iterator

from .base import Match

# (compiled pattern, banner_class, finding_type, label)
_PRIVILEGE = "privilege"
_CONFIDENTIAL = "confidentiality"

_PATTERNS: list[tuple[re.Pattern, str, str]] = [
    # --- privilege class ---
    (re.compile(r"(?i)attorney[-\s]*client\s+(?:privilege[d]?|communication[s]?)"),
     _PRIVILEGE, "attorney-client privileged"),
    (re.compile(r"(?i)attorney\s+work[-\s]*product"), _PRIVILEGE, "attorney work product"),
    (re.compile(r"(?i)\bwork[-\s]*product\s+(?:doctrine|privilege|protected)"),
     _PRIVILEGE, "work product"),
    (re.compile(r"(?i)privileged\s+(?:and|&)\s+confidential"),
     _PRIVILEGE, "privileged and confidential"),
    (re.compile(r"(?i)\bprivileged\s+communication"), _PRIVILEGE, "privileged communication"),
    # --- confidentiality class ---
    (re.compile(r"(?i)subject\s+to\s+(?:a\s+)?protective\s+order"),
     _CONFIDENTIAL, "subject to protective order"),
    (re.compile(r"(?i)strictly\s+confidential"), _CONFIDENTIAL, "strictly confidential"),
    (re.compile(r"(?i)confidential\s+(?:and|&)\s+proprietary"),
     _CONFIDENTIAL, "confidential and proprietary"),
    (re.compile(r"(?i)confidentiality\s+notice"), _CONFIDENTIAL, "confidentiality notice"),
    # Bare CONFIDENTIAL only when ALL CAPS in the source (case-sensitive).
    (re.compile(r"\bCONFIDENTIAL\b"), _CONFIDENTIAL, "confidential"),
]


def detect_all(text: str) -> list[Match]:
    out: list[Match] = []
    seen: set[tuple[int, int]] = set()
    # Try the more specific phrases first so a bare-CONFIDENTIAL match does not shadow
    # "PRIVILEGED AND CONFIDENTIAL".
    for pat, banner_class, label in _PATTERNS:
        for m in pat.finditer(text):
            span = (m.start(), m.end())
            # Skip if this span is already inside a recorded (more specific) banner.
            if any(s <= span[0] and span[1] <= e for (s, e) in seen):
                continue
            seen.add(span)
            ftype = ("privilege_banner" if banner_class == _PRIVILEGE
                     else "confidentiality_banner")
            out.append(Match(
                detector=f"banner.{banner_class}",
                type=ftype,
                start=span[0],
                end=span[1],
                raw=m.group(0),
                preview=m.group(0),
                detail={"banner_class": banner_class, "marker": label},
            ))
    return out
