"""PII Detection and Masking utilities for SDK."""

from __future__ import annotations

import hashlib
import re
from dataclasses import dataclass
from typing import Any, Optional


@dataclass
class PIIMatch:
    """Represents a detected PII match."""

    pii_type: str
    value: str
    start: int
    end: int
    confidence: float = 1.0


# PII Detection Patterns
PII_PATTERNS = {
    "email": {
        "pattern": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
        "mask": "[EMAIL]",
        "confidence": 0.95,
    },
    "phone": {
        # International and Japanese phone formats
        "pattern": r"\b(?:\+?[0-9]{1,4}[-.\s]?)?(?:\(?[0-9]{2,4}\)?[-.\s]?)?[0-9]{2,4}[-.\s]?[0-9]{3,4}[-.\s]?[0-9]{3,4}\b",
        "mask": "[PHONE]",
        "confidence": 0.85,
    },
    "credit_card": {
        # Major credit card formats (Visa, Mastercard, Amex, etc.)
        "pattern": r"\b(?:4[0-9]{3}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|5[1-5][0-9]{2}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|3[47][0-9]{2}[-\s]?[0-9]{6}[-\s]?[0-9]{5}|6(?:011|5[0-9]{2})[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4})\b",
        "mask": "[CREDIT_CARD]",
        "confidence": 0.95,
    },
    "ssn": {
        # US Social Security Number and Japanese My Number
        "pattern": r"\b(?:[0-9]{3}[-\s]?[0-9]{2}[-\s]?[0-9]{4}|[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4})\b",
        "mask": "[SSN]",
        "confidence": 0.90,
    },
    "ip_address": {
        # IPv4 and IPv6
        "pattern": r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}\b",
        "mask": "[IP_ADDRESS]",
        "confidence": 0.95,
    },
    "name": {
        # Japanese names (simplified pattern)
        "pattern": r"[一-龯]{1,4}[\s　][一-龯]{1,4}",
        "mask": "[NAME]",
        "confidence": 0.70,
    },
}


class PIIDetector:
    """PII Detection and Masking class."""

    def __init__(
        self,
        enabled_types: Optional[list[str]] = None,
        action: str = "mask",
        custom_patterns: Optional[list[dict]] = None,
    ):
        """
        Initialize PII Detector.

        Args:
            enabled_types: List of PII types to detect. None means all.
            action: Action to take - "mask", "redact", "hash", "none"
            custom_patterns: List of custom pattern dicts with 'name', 'pattern', 'mask' keys
        """
        self.enabled_types = enabled_types or list(PII_PATTERNS.keys())
        self.action = action
        self.custom_patterns = custom_patterns or []

        # Compile patterns
        self.compiled_patterns: dict[str, dict] = {}
        for pii_type in self.enabled_types:
            if pii_type in PII_PATTERNS:
                pattern_info = PII_PATTERNS[pii_type]
                self.compiled_patterns[pii_type] = {
                    "regex": re.compile(pattern_info["pattern"], re.IGNORECASE),
                    "mask": pattern_info["mask"],
                    "confidence": pattern_info["confidence"],
                }

        # Add custom patterns
        for custom in self.custom_patterns:
            name = custom.get("name", f"custom_{len(self.compiled_patterns)}")
            pattern = custom.get("pattern")
            mask = custom.get("mask", f"[{name.upper()}]")
            if pattern:
                try:
                    self.compiled_patterns[name] = {
                        "regex": re.compile(pattern, re.IGNORECASE),
                        "mask": mask,
                        "confidence": custom.get("confidence", 0.8),
                    }
                except re.error:
                    pass  # Skip invalid patterns

    def detect(self, text: str) -> list[PIIMatch]:
        """
        Detect PII in text.

        Args:
            text: Text to scan for PII

        Returns:
            List of PIIMatch objects
        """
        if not text:
            return []

        matches: list[PIIMatch] = []
        for pii_type, pattern_info in self.compiled_patterns.items():
            for match in pattern_info["regex"].finditer(text):
                matches.append(
                    PIIMatch(
                        pii_type=pii_type,
                        value=match.group(),
                        start=match.start(),
                        end=match.end(),
                        confidence=pattern_info["confidence"],
                    )
                )

        # Sort by position and remove overlapping matches (keep higher confidence)
        matches.sort(key=lambda m: (m.start, -m.confidence))
        filtered_matches: list[PIIMatch] = []
        last_end = -1
        for match in matches:
            if match.start >= last_end:
                filtered_matches.append(match)
                last_end = match.end

        return filtered_matches

    def mask_text(self, text: str) -> tuple[str, list[PIIMatch]]:
        """
        Detect and mask PII in text.

        Args:
            text: Text to process

        Returns:
            Tuple of (masked_text, list of matches)
        """
        matches = self.detect(text)
        if not matches:
            return text, []

        # Apply masking based on action
        result = text
        offset = 0

        for match in sorted(matches, key=lambda m: m.start):
            start = match.start + offset
            end = match.end + offset

            if self.action == "mask":
                replacement = self.compiled_patterns[match.pii_type]["mask"]
            elif self.action == "redact":
                replacement = "***"
            elif self.action == "hash":
                hash_value = hashlib.sha256(match.value.encode()).hexdigest()[:12]
                replacement = f"[HASH:{hash_value}]"
            else:  # none
                continue

            result = result[:start] + replacement + result[end:]
            offset += len(replacement) - len(match.value)

        return result, matches

    def process_json(
        self, data: Any, path: str = ""
    ) -> tuple[Any, list[tuple[str, PIIMatch]]]:
        """
        Recursively process JSON data for PII.

        Args:
            data: JSON data (dict, list, or primitive)
            path: Current path in the JSON structure

        Returns:
            Tuple of (processed_data, list of (path, match) tuples)
        """
        all_matches: list[tuple[str, PIIMatch]] = []

        if isinstance(data, dict):
            result = {}
            for key, value in data.items():
                new_path = f"{path}.{key}" if path else key
                processed, matches = self.process_json(value, new_path)
                result[key] = processed
                all_matches.extend(matches)
            return result, all_matches

        elif isinstance(data, list):
            result = []
            for i, item in enumerate(data):
                new_path = f"{path}[{i}]"
                processed, matches = self.process_json(item, new_path)
                result.append(processed)
                all_matches.extend(matches)
            return result, all_matches

        elif isinstance(data, str):
            masked_text, matches = self.mask_text(data)
            for match in matches:
                all_matches.append((path, match))
            return masked_text, all_matches

        else:
            return data, []
