"""PII (Personally Identifiable Information) detection and redaction."""

import re
from dataclasses import dataclass


@dataclass
class PIIMatch:
    """A detected PII instance."""

    pii_type: str
    value: str
    start: int
    end: int


# Patterns: (name, compiled regex)
_PII_PATTERNS: list[tuple[str, re.Pattern]] = [
    (
        "email",
        re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"),
    ),
    (
        "phone_us",
        re.compile(r"\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b"),
    ),
    (
        "phone_intl",
        re.compile(r"\b\+\d{1,3}[-.\s]?\d{4,14}\b"),
    ),
    (
        "ssn",
        re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),
    ),
    (
        "credit_card",
        re.compile(r"\b(?:\d{4}[-\s]?){3}\d{4}\b"),
    ),
    (
        "ipv4",
        re.compile(r"\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b"),
    ),
    (
        "iban",
        re.compile(r"\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b"),
    ),
    (
        "us_passport",
        re.compile(r"\b[A-Z]\d{8}\b"),
    ),
    (
        "aws_key",
        re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
    ),
    (
        "generic_api_key",
        re.compile(
            r"\b(?:api[_-]?key|apikey|secret[_-]?key|access[_-]?token)\s*[:=]\s*['\"]?([A-Za-z0-9_\-]{20,})['\"]?",
            re.IGNORECASE,
        ),
    ),
]


class PIIScanner:
    """Scans text for PII using regex patterns."""

    def scan(self, text: str) -> list[PIIMatch]:
        """Return all PII matches found in text."""
        matches: list[PIIMatch] = []
        for pii_type, pattern in _PII_PATTERNS:
            for m in pattern.finditer(text):
                matches.append(
                    PIIMatch(
                        pii_type=pii_type,
                        value=m.group(),
                        start=m.start(),
                        end=m.end(),
                    )
                )
        return matches

    def has_pii(self, text: str) -> bool:
        """Check if text contains any PII."""
        return any(pattern.search(text) for _, pattern in _PII_PATTERNS)

    def redact(self, text: str) -> str:
        """Replace PII in text with redaction markers."""
        result = text
        # Process matches in reverse order to preserve positions
        matches = sorted(self.scan(text), key=lambda m: m.start, reverse=True)
        for match in matches:
            replacement = f"[{match.pii_type.upper()}_REDACTED]"
            result = result[: match.start] + replacement + result[match.end :]
        return result
