"""PII 检测与脱敏器.

检测并脱敏以下 PII 信息：
  - 邮箱地址
  - 手机号（中国大陆 11 位）
  - 身份证号（18 位）
  - 银行卡号（16-19 位）
  - IP 地址

脱敏方式：保留首尾字符，中间用 * 替换。
"""

import re
from dataclasses import dataclass, field

import structlog

logger = structlog.get_logger()

# PII 检测正则表达式
PII_PATTERNS: dict[str, re.Pattern[str]] = {
    "email": re.compile(
        r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
    ),
    "phone": re.compile(
        r"(?<!\d)1[3-9]\d{9}(?!\d)",
    ),
    "id_card": re.compile(
        r"(?<!\d)[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])"
        r"(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx](?!\d)",
    ),
    "bank_card": re.compile(
        r"(?<!\d)[1-9]\d{15,18}(?!\d)",
    ),
    "ip_address": re.compile(
        r"\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}"
        r"(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b",
    ),
}


@dataclass
class RedactionResult:
    """脱敏结果."""

    redacted_text: str
    found_types: list[str] = field(default_factory=list)
    redaction_count: int = 0


class PIIRedactor:
    """PII 检测与脱敏器."""

    def redact(self, text: str) -> RedactionResult:
        """检测并脱敏文本中的 PII 信息.

        Args:
            text: 原始文本

        Returns:
            RedactionResult（含脱敏后文本 + 检测到的 PII 类型）
        """
        redacted_text = text
        found_types: list[str] = []
        total_count = 0

        for pii_type, pattern in PII_PATTERNS.items():
            matches = pattern.findall(redacted_text)
            if matches:
                found_types.append(pii_type)
                total_count += len(matches)
                redacted_text = pattern.sub(
                    lambda m: self._mask(m.group()), redacted_text,
                )

        if total_count > 0:
            logger.info(
                "pii_redacted",
                types=found_types,
                count=total_count,
            )

        return RedactionResult(
            redacted_text=redacted_text,
            found_types=found_types,
            redaction_count=total_count,
        )

    def detect(self, text: str) -> list[str]:
        """仅检测 PII 类型（不脱敏）.

        Args:
            text: 原始文本

        Returns:
            检测到的 PII 类型列表
        """
        found: list[str] = []
        for pii_type, pattern in PII_PATTERNS.items():
            if pattern.search(text):
                found.append(pii_type)
        return found

    @staticmethod
    def _mask(value: str) -> str:
        """脱敏单个值（保留首尾，中间用 * 替换）."""
        if len(value) <= 2:
            return "*" * len(value)
        if len(value) <= 6:
            return value[0] + "*" * (len(value) - 2) + value[-1]
        # 保留前 2 后 2
        return value[:2] + "*" * (len(value) - 4) + value[-2:]
