"""输入清洗器（prompt injection 检测 + 危险字符过滤）.

检测以下攻击模式：
  - Prompt injection：jailbreak 指令、角色覆盖、指令注入
  - 危险字符：null bytes、控制字符
  - 超长输入：防止 DoS

检测到 prompt injection 时抛 AIError(AI_PROMPT_INJECTION_DETECTED)。
"""

import re
from dataclasses import dataclass, field

import structlog

from ..errors import AIError, ErrorCode

logger = structlog.get_logger()

# Prompt injection 检测模式
INJECTION_PATTERNS: list[re.Pattern[str]] = [
    # 忽略之前所有指令
    re.compile(r"ignore\s+(?:all\s+)?(?:previous|prior|above)\s+instructions?", re.IGNORECASE),
    re.compile(r" disreg(?:ard|aard)\s+(?:all\s+)?(?:previous|prior)", re.IGNORECASE),
    # 角色覆盖
    re.compile(r"you\s+are\s+(?:now|actually)\s+(?:a|an)?\s*(?:different|new)", re.IGNORECASE),
    re.compile(r"pretend\s+(?:you\s+are|to\s+be)\s+(?:a|an)?\s*(?:different|new)", re.IGNORECASE),
    # 系统提示泄露
    re.compile(
        r"(?:show|reveal|print|output)\s+(?:me\s+)?(?:your\s+)?"
        r"(?:system|initial)\s+prompt",
        re.IGNORECASE,
    ),
    re.compile(
        r"(?:what|how)\s+(?:are|were)\s+you\s+"
        r"(?:instructed|programmed|configured)",
        re.IGNORECASE,
    ),
    # 越狱
    re.compile(r"(?:jailbreak|DAN|do\s+anything\s+now)", re.IGNORECASE),
    # 开发模式
    re.compile(r"(?:enter|enable|activate)\s+(?:developer|god|admin|root)\s+mode", re.IGNORECASE),
]

# 危险字符模式
DANGEROUS_CHARS = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")

# 最大输入长度（单字段）
MAX_INPUT_LENGTH = 8000


@dataclass
class SanitizeResult:
    """清洗结果."""

    sanitized_text: str
    is_safe: bool = True
    injection_detected: bool = False
    warnings: list[str] = field(default_factory=list)


class InputSanitizer:
    """输入清洗器."""

    def sanitize(
        self,
        text: str,
        max_length: int = MAX_INPUT_LENGTH,
        strict: bool = False,
    ) -> SanitizeResult:
        """清洗输入文本.

        Args:
            text: 原始文本
            max_length: 最大允许长度
            strict: 严格模式（检测到 injection 时抛异常）

        Returns:
            SanitizeResult（含清洗后文本 + 安全标记）

        Raises:
            AIError(AI_PROMPT_INJECTION_DETECTED): strict=True 且检测到 injection
        """
        result = SanitizeResult(sanitized_text=text, is_safe=True)

        # 1. 去除危险控制字符
        if DANGEROUS_CHARS.search(text):
            result.sanitized_text = DANGEROUS_CHARS.sub("", text)
            result.warnings.append("dangerous control characters removed")

        # 2. 长度检查
        if len(text) > max_length:
            result.sanitized_text = result.sanitized_text[:max_length]
            result.warnings.append(
                f"input truncated to {max_length} characters",
            )

        # 3. Prompt injection 检测
        for pattern in INJECTION_PATTERNS:
            if pattern.search(text):
                result.injection_detected = True
                result.is_safe = False
                result.warnings.append(
                    f"prompt injection pattern detected: {pattern.pattern[:50]}",
                )
                logger.warning(
                    "prompt_injection_detected",
                    pattern=pattern.pattern[:100],
                )
                break

        if result.injection_detected and strict:
            raise AIError(
                ErrorCode.AI_PROMPT_INJECTION_DETECTED,
                "Potential prompt injection detected in input",
                {"warnings": result.warnings},
            )

        return result

    def is_safe(self, text: str) -> bool:
        """快速检查输入是否安全（不修改文本）."""
        for pattern in INJECTION_PATTERNS:
            if pattern.search(text):
                return False
        return not DANGEROUS_CHARS.search(text)
