"""Security-focused analysis for issue detection.

Detects potential security issues in captured I/O data:
- Sensitive data in responses (emails, tokens, SSNs)
- Missing security headers
- SQL injection patterns
- Leaked credentials in request bodies
"""

from __future__ import annotations

import re
from typing import TYPE_CHECKING, List

if TYPE_CHECKING:
    from autodebug.detection.transcript_analyzer import (
        HTTPRecord,
        SQLRecord,
        Transcript,
        TranscriptInsight,
    )


# Patterns for sensitive data detection
_EMAIL_RE = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
_SSN_RE = re.compile(r"\b\d{3}-\d{2}-\d{4}\b")
_SSN_CONTEXT_RE = re.compile(
    r"(?:ssn|social.?security|tax.?id|social.?sec)",
    re.IGNORECASE,
)
_CREDIT_CARD_RE = re.compile(r"\b(?:\d[ -]*?){13,19}\b")
_JWT_RE = re.compile(r"eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}")
_API_KEY_RE = re.compile(
    r"(?:api[_-]?key|apikey|secret[_-]?key|access[_-]?token|auth[_-]?token"
    r"|sk_(?:live|test)_|ghp_|glpat_|npm_)"
    r"\s*[:=]?\s*['\"]?([A-Za-z0-9_\-]{16,})['\"]?",
    re.IGNORECASE,
)
_PRIVATE_KEY_RE = re.compile(r"-----BEGIN (?:RSA |EC )?PRIVATE KEY-----")
_PASSWORD_FIELD_RE = re.compile(
    r"[\"'](?:password|passwd|pwd|secret|credential)[\"']\s*:\s*[\"'][^\"']+[\"']",
    re.IGNORECASE,
)
_BEARER_TOKEN_RE = re.compile(
    r"(?:Bearer|Authorization)\s+[A-Za-z0-9_\-\.]{20,}",
    re.IGNORECASE,
)
_OAUTH_TOKEN_RE = re.compile(
    r"(?:access_token|refresh_token|oauth_token)\s*[=:]\s*['\"]?[A-Za-z0-9_\-\.]{16,}",
    re.IGNORECASE,
)

# SQL injection patterns
_SQLI_PATTERNS = [
    re.compile(r"(?:--|;)\s*(?:DROP|ALTER|TRUNCATE)\s+TABLE", re.IGNORECASE),
    re.compile(r"'\s*(?:OR|AND)\s+['\d].*=", re.IGNORECASE),
    re.compile(r"UNION\s+(?:ALL\s+)?SELECT", re.IGNORECASE),
    re.compile(r";\s*(?:INSERT|UPDATE|DELETE)\s+", re.IGNORECASE),
    re.compile(r"(?:SLEEP|WAITFOR|BENCHMARK|PG_SLEEP)\s*\(", re.IGNORECASE),
    re.compile(r"/\*.*?\*/", re.IGNORECASE),
    re.compile(r";\s*(?:EXEC|CALL|DECLARE)\s+", re.IGNORECASE),
    re.compile(r"\bOR\s+1=1\b", re.IGNORECASE),
    re.compile(r"\bOR\s+'1'='1'\b", re.IGNORECASE),
    re.compile(r"\bOR\s+\"1\"=\"1\"\b", re.IGNORECASE),
    re.compile(r"\bAND\s+1=1\b", re.IGNORECASE),
]


def _is_luhn_valid(number_str: str) -> bool:
    """Validate a credit card number using the Luhn algorithm."""
    digits = [int(d) for d in number_str if d.isdigit()]
    if len(digits) < 13 or len(digits) > 19:
        return False
    checksum = 0
    for i, digit in enumerate(reversed(digits)):
        if i % 2 == 1:
            digit *= 2
            if digit > 9:
                digit -= 9
        checksum += digit
    return checksum % 10 == 0


def _looks_like_token(text: str) -> bool:
    """Heuristic to reduce false positives for API key detection."""
    match = _API_KEY_RE.search(text)
    if not match:
        return False
    token = match.group(1) or ""
    if len(token) < 20:
        return False
    if len(set(token)) < 6:
        return False
    return True

# Required security headers
SECURITY_HEADERS = {
    "strict-transport-security": "Add HSTS header to enforce HTTPS",
    "x-content-type-options": "Add X-Content-Type-Options: nosniff",
    "x-frame-options": "Add X-Frame-Options to prevent clickjacking",
    "content-security-policy": "Add CSP header to prevent XSS and injection attacks",
    "x-xss-protection": "Add X-XSS-Protection: 1; mode=block",
    "referrer-policy": "Add Referrer-Policy to control referrer information",
}


class SecurityAnalyzer:
    """Analyze transcript data for security issues."""

    def analyze(self, transcript: Transcript) -> List[TranscriptInsight]:

        insights: List[TranscriptInsight] = []

        for record in transcript.http_records:
            insights.extend(self._check_sensitive_data_in_response(record))
            insights.extend(self._check_sensitive_data_in_request(record))
            insights.extend(self._check_security_headers(record))

        for record in transcript.sql_records:
            insights.extend(self._check_sql_injection(record))

        return insights

    def _check_sensitive_data_in_response(self, record: HTTPRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        body = record.response_body
        if not body:
            return []

        findings: List[tuple] = []

        ssn_match = _SSN_RE.search(body)
        if ssn_match and _SSN_CONTEXT_RE.search(body[max(0, ssn_match.start() - 50):ssn_match.end() + 50]):
            findings.append(("SSN", 5, "Social Security Numbers detected in response"))
        cc_match = _CREDIT_CARD_RE.search(body)
        if cc_match and _is_luhn_valid(cc_match.group(0)):
            findings.append(("credit_card", 5, "Credit card numbers detected in response"))
        if _PRIVATE_KEY_RE.search(body):
            findings.append(("private_key", 5, "Private key material detected in response"))
        if _JWT_RE.search(body):
            findings.append(("jwt_token", 5, "JWT token exposed in response body"))
        if _API_KEY_RE.search(body) and _looks_like_token(body):
            findings.append(("api_key", 4, "API key/secret exposed in response body"))
        if _BEARER_TOKEN_RE.search(body):
            findings.append(("bearer_token", 5, "Bearer/OAuth token exposed in response body"))
        if _OAUTH_TOKEN_RE.search(body):
            findings.append(("oauth_token", 5, "OAuth token exposed in response body"))

        insights: List[TranscriptInsight] = []
        for data_type, severity, desc in findings:
            insights.append(
                TranscriptInsight(
                    category="sensitive_data_exposure",
                    severity=severity,
                    description=f"{desc} from {record.url_canonical}",
                    evidence={
                        "data_type": data_type,
                        "url": record.url_canonical,
                        "direction": "response",
                    },
                    related_records=[record.seq],
                    suggested_investigation="Remove or mask sensitive data before sending in response",
                )
            )

        return insights

    def _check_sensitive_data_in_request(self, record: HTTPRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        body = record.request_body
        if not body:
            return []

        insights: List[TranscriptInsight] = []

        if _PASSWORD_FIELD_RE.search(body):
            insights.append(
                TranscriptInsight(
                    category="credentials_in_request",
                    severity=4,
                    description=f"Password/credential sent in request to {record.url_canonical}",
                    evidence={
                        "url": record.url_canonical,
                        "method": record.method,
                    },
                    related_records=[record.seq],
                    suggested_investigation="Ensure this endpoint uses HTTPS and doesn't log request bodies",
                )
            )

        if _PRIVATE_KEY_RE.search(body):
            insights.append(
                TranscriptInsight(
                    category="private_key_in_request",
                    severity=5,
                    description=f"Private key sent in request to {record.url_canonical}",
                    evidence={"url": record.url_canonical},
                    related_records=[record.seq],
                    suggested_investigation="Never send private keys in HTTP requests",
                )
            )

        return insights

    def _check_security_headers(self, record: HTTPRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        if record.status >= 400:
            return []

        if not record.headers:
            return []

        headers_lower = {k.lower(): v for k, v in record.headers.items()}

        missing = [h for h in SECURITY_HEADERS if h not in headers_lower]
        if not missing:
            return []

        return [
            TranscriptInsight(
                category="missing_security_header",
                severity=2,
                description=f"Missing {len(missing)} security header(s) from {record.url_canonical}",
                evidence={
                    "missing_headers": missing,
                    "url": record.url_canonical,
                },
                related_records=[record.seq],
                suggested_investigation="; ".join(
                    SECURITY_HEADERS[h] for h in missing
                ),
            )
        ]

    def _check_sql_injection(self, record: SQLRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        sql = record.sql_canonical
        if not sql:
            return []

        matched_patterns = []
        for pattern in _SQLI_PATTERNS:
            if pattern.search(sql):
                matched_patterns.append(pattern.pattern[:100])

        if not matched_patterns:
            return []

        return [
            TranscriptInsight(
                category="sql_injection_risk",
                severity=5,
                description=f"Potential SQL injection: {len(matched_patterns)} pattern(s) detected",
                evidence={
                    "query": sql[:200],
                    "patterns": matched_patterns,
                },
                related_records=[record.seq],
                suggested_investigation="Use parameterized queries instead of string interpolation",
            )
        ]
