"""
Content Redactor for SparkNet.

Helps users automatically or manually redact sensitive information
from content before sharing.
"""

from dataclasses import dataclass
from enum import Enum
from typing import List, Optional, Dict, Tuple

from .scanner import SecurityScanner, ScanResult, ScanFinding, FindingSeverity


class RedactionMode(Enum):
    """How to redact sensitive content."""
    FULL = "full"          # Replace entire match with [REDACTED]
    PARTIAL = "partial"    # Show first/last few chars: sk-ab****23
    TYPE_LABEL = "type"    # Replace with type: [API_KEY]
    CUSTOM = "custom"      # User-provided replacement


@dataclass
class RedactionResult:
    """Result of redacting content."""
    original_content: str
    redacted_content: str
    redactions_made: int
    findings_redacted: List[ScanFinding]

    @property
    def was_modified(self) -> bool:
        return self.redactions_made > 0


class Redactor:
    """
    Redacts sensitive information from content.

    Usage:
        redactor = Redactor()
        result = redactor.redact("My key is sk-abc123xyz")
        print(result.redacted_content)
        # "My key is [REDACTED]"
    """

    def __init__(
        self,
        scanner: Optional[SecurityScanner] = None,
        default_mode: RedactionMode = RedactionMode.TYPE_LABEL,
        min_severity: FindingSeverity = FindingSeverity.HIGH,
    ):
        """
        Initialize the redactor.

        Args:
            scanner: SecurityScanner to use (creates default if None)
            default_mode: How to redact by default
            min_severity: Only redact findings at this severity or higher
        """
        self.scanner = scanner or SecurityScanner()
        self.default_mode = default_mode
        self.min_severity = min_severity

        # Severity ordering for comparison
        self._severity_order = {
            FindingSeverity.CRITICAL: 0,
            FindingSeverity.HIGH: 1,
            FindingSeverity.MEDIUM: 2,
            FindingSeverity.LOW: 3,
            FindingSeverity.INFO: 4,
        }

    def redact(
        self,
        content: str,
        mode: Optional[RedactionMode] = None,
        custom_replacements: Optional[Dict[str, str]] = None,
    ) -> RedactionResult:
        """
        Redact sensitive information from content.

        Args:
            content: The content to redact
            mode: Redaction mode (uses default if None)
            custom_replacements: Map of matched_text -> replacement

        Returns:
            RedactionResult with redacted content
        """
        mode = mode or self.default_mode
        custom_replacements = custom_replacements or {}

        # Scan for sensitive content
        scan_result = self.scanner.scan(content)

        if scan_result.is_clean:
            return RedactionResult(
                original_content=content,
                redacted_content=content,
                redactions_made=0,
                findings_redacted=[],
            )

        # Filter by severity
        min_order = self._severity_order[self.min_severity]
        findings_to_redact = [
            f for f in scan_result.findings
            if self._severity_order[f.severity] <= min_order
        ]

        if not findings_to_redact:
            return RedactionResult(
                original_content=content,
                redacted_content=content,
                redactions_made=0,
                findings_redacted=[],
            )

        # Sort findings by position (reverse) so we can replace from end to start
        # This preserves position indices
        sorted_findings = sorted(
            findings_to_redact,
            key=lambda f: f.start_position,
            reverse=True,
        )

        # Perform redactions
        redacted = content
        for finding in sorted_findings:
            replacement = self._get_replacement(finding, mode, custom_replacements)
            redacted = (
                redacted[:finding.start_position] +
                replacement +
                redacted[finding.end_position:]
            )

        return RedactionResult(
            original_content=content,
            redacted_content=redacted,
            redactions_made=len(sorted_findings),
            findings_redacted=findings_to_redact,
        )

    def _get_replacement(
        self,
        finding: ScanFinding,
        mode: RedactionMode,
        custom_replacements: Dict[str, str],
    ) -> str:
        """Get the replacement text for a finding."""
        # Check for custom replacement first
        if finding.matched_text in custom_replacements:
            return custom_replacements[finding.matched_text]

        if mode == RedactionMode.FULL:
            return "[REDACTED]"

        elif mode == RedactionMode.PARTIAL:
            text = finding.matched_text
            if len(text) <= 8:
                return "*" * len(text)
            return text[:4] + "*" * (len(text) - 8) + text[-4:]

        elif mode == RedactionMode.TYPE_LABEL:
            type_labels = {
                "api_key": "[API_KEY]",
                "password": "[PASSWORD]",
                "token": "[TOKEN]",
                "pii_email": "[EMAIL]",
                "pii_phone": "[PHONE]",
                "pii_ssn": "[SSN]",
                "pii_credit_card": "[CREDIT_CARD]",
                "file_path": "[PATH]",
                "private_key": "[PRIVATE_KEY]",
                "connection_string": "[CONNECTION_STRING]",
                "custom": "[SENSITIVE]",
            }
            return type_labels.get(finding.finding_type.value, "[REDACTED]")

        else:  # CUSTOM without specific replacement
            return "[REDACTED]"

    def suggest_redaction(self, content: str) -> List[Dict]:
        """
        Suggest redactions without applying them.

        Returns list of suggestions for user review.
        """
        scan_result = self.scanner.scan(content)

        suggestions = []
        for finding in scan_result.findings:
            suggestions.append({
                "original": finding.redacted_text,  # Partially hidden
                "full_text": finding.matched_text,   # For comparison only
                "position": {
                    "start": finding.start_position,
                    "end": finding.end_position,
                },
                "type": finding.finding_type.value,
                "severity": finding.severity.value,
                "rule": finding.rule_name,
                "suggested_replacement": self._get_replacement(
                    finding, self.default_mode, {}
                ),
            })

        return suggestions


def redact_content(
    content: str,
    mode: RedactionMode = RedactionMode.TYPE_LABEL,
) -> str:
    """
    Convenience function to redact content.

    Returns the redacted string directly.
    """
    redactor = Redactor(default_mode=mode)
    result = redactor.redact(content)
    return result.redacted_content


def auto_clean(content: str) -> Tuple[str, int]:
    """
    Automatically clean content of all sensitive data.

    Returns (cleaned_content, number_of_redactions).
    """
    redactor = Redactor(
        default_mode=RedactionMode.TYPE_LABEL,
        min_severity=FindingSeverity.LOW,  # Redact almost everything
    )
    result = redactor.redact(content)
    return result.redacted_content, result.redactions_made
