"""
AIPTX Beast Mode - WAF Fingerprinter
====================================

Active and passive WAF fingerprinting using signature matching
and behavioral analysis.
"""

from __future__ import annotations

import logging
import re
from dataclasses import dataclass, field
from typing import Any, Optional

from aipt_v2.exploitation.waf.signatures import WAF_SIGNATURES_DB, WAFSignatureEntry

logger = logging.getLogger(__name__)


@dataclass
class FingerprintResult:
    """Result of WAF fingerprinting."""
    waf_detected: bool
    waf_id: str | None
    waf_name: str | None
    confidence: float
    evidence: list[str]
    difficulty: int
    recommendations: list[str] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "waf_detected": self.waf_detected,
            "waf_id": self.waf_id,
            "waf_name": self.waf_name,
            "confidence": self.confidence,
            "evidence": self.evidence,
            "difficulty": self.difficulty,
            "recommendations": self.recommendations,
        }


class WAFFingerprinter:
    """
    WAF fingerprinting engine.

    Combines passive analysis (response inspection) with optional
    active probing to accurately identify WAF type.
    """

    def __init__(self):
        """Initialize the fingerprinter."""
        self._signatures = WAF_SIGNATURES_DB
        self._probe_payloads = [
            # Generic probes that trigger most WAFs
            "<script>alert(1)</script>",
            "' OR '1'='1",
            "../../../etc/passwd",
            "; cat /etc/passwd",
            "${7*7}",
        ]

    def fingerprint_passive(
        self,
        responses: list[dict[str, Any]],
    ) -> FingerprintResult:
        """
        Passive fingerprinting from existing responses.

        Args:
            responses: List of HTTP responses with status_code, headers, body

        Returns:
            FingerprintResult
        """
        scores: dict[str, tuple[float, list[str]]] = {}

        for response in responses:
            status_code = response.get("status_code", 200)
            headers = {k.lower(): v for k, v in response.get("headers", {}).items()}
            body = response.get("body", "")
            cookies = response.get("cookies", {})

            for waf_id, signature in self._signatures.items():
                if waf_id not in scores:
                    scores[waf_id] = (0.0, [])

                score, evidence = scores[waf_id]

                # Check headers
                for pattern in signature.detection_patterns.get("headers", []):
                    for header_name, header_value in headers.items():
                        if self._matches(header_name, pattern) or self._matches(header_value, pattern):
                            score += 0.2
                            evidence.append(f"Header match: {header_name}")

                # Check server header
                for pattern in signature.detection_patterns.get("server", []):
                    if self._matches(headers.get("server", ""), pattern):
                        score += 0.15
                        evidence.append(f"Server header match")

                # Check cookies
                for pattern in signature.detection_patterns.get("cookies", []):
                    for cookie_name in cookies.keys():
                        if self._matches(cookie_name, pattern):
                            score += 0.15
                            evidence.append(f"Cookie match: {cookie_name}")

                # Check body
                for pattern in signature.detection_patterns.get("body", []):
                    if self._matches(body, pattern):
                        score += 0.25
                        evidence.append(f"Body pattern match")

                # Check status code
                if status_code in signature.block_status_codes:
                    score += 0.1
                    evidence.append(f"Block status code: {status_code}")

                scores[waf_id] = (score, evidence)

        # Find best match
        best_waf_id = None
        best_score = 0.0
        best_evidence: list[str] = []

        for waf_id, (score, evidence) in scores.items():
            if score > best_score:
                best_score = score
                best_waf_id = waf_id
                best_evidence = evidence

        if best_waf_id and best_score >= 0.3:
            signature = self._signatures[best_waf_id]
            return FingerprintResult(
                waf_detected=True,
                waf_id=best_waf_id,
                waf_name=signature.name,
                confidence=min(1.0, best_score),
                evidence=list(set(best_evidence)),
                difficulty=signature.difficulty_rating,
                recommendations=signature.known_weaknesses,
            )

        return FingerprintResult(
            waf_detected=False,
            waf_id=None,
            waf_name=None,
            confidence=0.0,
            evidence=[],
            difficulty=0,
        )

    def fingerprint_from_block(
        self,
        status_code: int,
        headers: dict[str, str],
        body: str,
        cookies: dict[str, str] | None = None,
    ) -> FingerprintResult:
        """
        Fingerprint from a blocked response.

        Args:
            status_code: HTTP status code
            headers: Response headers
            body: Response body
            cookies: Response cookies

        Returns:
            FingerprintResult
        """
        response = {
            "status_code": status_code,
            "headers": headers,
            "body": body,
            "cookies": cookies or {},
        }
        return self.fingerprint_passive([response])

    def get_probe_payloads(self) -> list[str]:
        """Get payloads for active probing."""
        return self._probe_payloads.copy()

    def _matches(self, value: str, pattern: str | re.Pattern) -> bool:
        """Check if value matches pattern."""
        if not value:
            return False

        if isinstance(pattern, re.Pattern):
            return bool(pattern.search(value))
        else:
            return pattern.lower() in value.lower()


def fingerprint_waf(responses: list[dict[str, Any]]) -> FingerprintResult:
    """Convenience function to fingerprint WAF."""
    fingerprinter = WAFFingerprinter()
    return fingerprinter.fingerprint_passive(responses)


__all__ = [
    "FingerprintResult",
    "WAFFingerprinter",
    "fingerprint_waf",
]
