"""
AIPTX Key Validation Bridge
===========================

Connects scan findings to automatic key validation.

This bridge:
1. Receives findings from the orchestrator
2. Detects API keys in finding evidence
3. Validates detected keys against APIs
4. Reports results with AI thinking display
"""

from __future__ import annotations

import asyncio
import logging
from typing import Any, Callable, Optional
from dataclasses import dataclass, field

from .base import APIKeyInfo, ValidationResult, KeyType, RiskLevel
from .detector import KeyDetector
from .thinking import KeyValidationThinking, get_thinking

# Validators
from .validators import (
    AWSKeyValidator,
    GCPKeyValidator,
    AzureKeyValidator,
    GitHubKeyValidator,
    GitLabKeyValidator,
    SlackKeyValidator,
    StripeKeyValidator,
    TwilioKeyValidator,
    SendGridKeyValidator,
    AtlassianKeyValidator,
)
from .validators.s3_scanner import S3BucketScanner

logger = logging.getLogger(__name__)


@dataclass
class KeyValidationConfig:
    """Configuration for key validation."""

    enabled: bool = True
    validate_in_realtime: bool = True
    show_ai_thinking: bool = True

    # Providers to validate
    cloud_providers: list[str] = field(
        default_factory=lambda: ["aws", "gcp", "azure"]
    )
    saas_providers: list[str] = field(
        default_factory=lambda: [
            "github", "gitlab", "slack", "stripe",
            "twilio", "sendgrid", "atlassian"
        ]
    )

    # Timeouts
    validation_timeout: float = 30.0
    enumeration_timeout: float = 60.0

    # Enumeration
    enumerate_permissions: bool = True
    max_resources: int = 100

    # S3 scanning
    scan_s3_buckets: bool = True


class KeyValidationBridge:
    """
    Bridge between scan findings and key validation.

    Integrates with the orchestrator to automatically detect and
    validate API keys found during scans.
    """

    # Vulnerability types that may contain API keys
    KEY_VULN_TYPES = [
        "HARDCODED_SECRETS",
        "SENSITIVE_DATA_EXPOSURE",
        "INFORMATION_DISCLOSURE",
        "CONFIG_EXPOSURE",
        "ENV_EXPOSURE",
        "GIT_EXPOSURE",
        "DEBUG_ENABLED",
        "API_KEY_EXPOSURE",
        "CREDENTIAL_LEAK",
    ]

    def __init__(
        self,
        config: Optional[KeyValidationConfig] = None,
        on_key_validated: Optional[Callable[[ValidationResult], None]] = None,
    ):
        """
        Initialize bridge.

        Args:
            config: Validation configuration
            on_key_validated: Callback when a key is validated
        """
        self.config = config or KeyValidationConfig()
        self.on_key_validated = on_key_validated

        # Initialize components
        self.detector = KeyDetector()
        self.thinking = get_thinking(enabled=self.config.show_ai_thinking)
        self.s3_scanner = S3BucketScanner(timeout=self.config.validation_timeout)

        # Initialize validators
        self.validators = self._init_validators()

        # State
        self.detected_keys: list[APIKeyInfo] = []
        self.validation_results: list[ValidationResult] = []
        self.seen_fingerprints: set[str] = set()

    def _init_validators(self) -> dict[KeyType, Any]:
        """Initialize validators for each key type."""
        validators = {}

        # Cloud providers
        if "aws" in self.config.cloud_providers:
            validators[KeyType.AWS] = AWSKeyValidator(timeout=self.config.validation_timeout)
        if "gcp" in self.config.cloud_providers:
            validators[KeyType.GCP] = GCPKeyValidator(timeout=self.config.validation_timeout)
        if "azure" in self.config.cloud_providers:
            validators[KeyType.AZURE] = AzureKeyValidator(timeout=self.config.validation_timeout)

        # SaaS providers
        if "github" in self.config.saas_providers:
            validators[KeyType.GITHUB] = GitHubKeyValidator(timeout=self.config.validation_timeout)
        if "gitlab" in self.config.saas_providers:
            validators[KeyType.GITLAB] = GitLabKeyValidator(timeout=self.config.validation_timeout)
        if "slack" in self.config.saas_providers:
            validators[KeyType.SLACK] = SlackKeyValidator(timeout=self.config.validation_timeout)
        if "stripe" in self.config.saas_providers:
            validators[KeyType.STRIPE] = StripeKeyValidator(timeout=self.config.validation_timeout)
        if "twilio" in self.config.saas_providers:
            validators[KeyType.TWILIO] = TwilioKeyValidator(timeout=self.config.validation_timeout)
        if "sendgrid" in self.config.saas_providers:
            validators[KeyType.SENDGRID] = SendGridKeyValidator(timeout=self.config.validation_timeout)
        if "atlassian" in self.config.saas_providers:
            validators[KeyType.ATLASSIAN] = AtlassianKeyValidator(timeout=self.config.validation_timeout)

        return validators

    async def on_finding(self, finding: Any) -> list[ValidationResult]:
        """
        Process a scan finding for API keys.

        This is the main entry point, called by the orchestrator
        when a new finding is discovered.

        Args:
            finding: Finding object from scanner

        Returns:
            List of validation results for detected keys
        """
        if not self.config.enabled:
            return []

        # Get evidence - skip if empty
        evidence = getattr(finding, "evidence", "") or ""
        if not evidence or len(evidence) < 10:
            return []  # No evidence worth analyzing

        # Check if finding type is relevant
        vuln_type = getattr(finding, "vuln_type", None)
        vuln_type_value = "UNKNOWN"
        if vuln_type:
            vuln_type_value = vuln_type.value if hasattr(vuln_type, "value") else str(vuln_type)
            # Only process relevant finding types
            if not any(kt in vuln_type_value.upper() for kt in self.KEY_VULN_TYPES):
                return []
        else:
            # No vuln_type - skip unless evidence looks like it might contain keys
            if not any(pattern in evidence.lower() for pattern in ["key", "token", "secret", "password", "api", "credential"]):
                return []

        # Detect keys from finding FIRST (before showing thinking)
        keys = self.detector.detect_from_finding(finding)

        if not keys:
            return []

        # Extract finding context for display
        vuln_title = getattr(finding, "title", "") or getattr(finding, "name", "") or ""
        target_url = getattr(finding, "url", "") or getattr(finding, "target", "") or ""
        severity = getattr(finding, "severity", "") or ""
        if hasattr(severity, "value"):
            severity = severity.value

        # Only show AI thinking if we actually found keys
        self.thinking.think_key_detection(
            vuln_type=vuln_type_value,
            vuln_title=vuln_title,
            target_url=target_url,
            severity=str(severity),
            evidence_preview=evidence[:50] if evidence else "",
        )

        # Filter duplicates
        new_keys = []
        for key in keys:
            fingerprint = key.fingerprint
            if fingerprint not in self.seen_fingerprints:
                self.seen_fingerprints.add(fingerprint)
                new_keys.append(key)
                self.detected_keys.append(key)
                self.thinking.think_key_found(key)

        # Validate keys if real-time validation is enabled
        results = []
        if self.config.validate_in_realtime and new_keys:
            results = await self.validate_keys(new_keys)

        return results

    async def validate_keys(
        self,
        keys: Optional[list[APIKeyInfo]] = None,
    ) -> list[ValidationResult]:
        """
        Validate detected API keys.

        Args:
            keys: List of keys to validate (defaults to all detected)

        Returns:
            List of validation results
        """
        keys = keys or self.detected_keys
        results = []

        for key in keys:
            validator = self.validators.get(key.key_type)
            if not validator:
                logger.debug(f"No validator for key type: {key.key_type}")
                continue

            self.thinking.think_validation_start(key)

            try:
                result = await validator.validate(key)
                results.append(result)
                self.validation_results.append(result)

                self.thinking.think_validation_result(result)

                if result.is_valid:
                    # Show risk assessment
                    self.thinking.think_risk_assessment(
                        result.risk_level,
                        result.exploitation_potential or "",
                        result.attack_vectors or [],
                    )

                # Callback
                if self.on_key_validated:
                    self.on_key_validated(result)

            except Exception as e:
                logger.error(f"Validation error for {key.key_type}: {e}")

        return results

    async def validate_from_text(self, text: str) -> list[ValidationResult]:
        """
        Detect and validate keys from raw text.

        Args:
            text: Text to scan for keys

        Returns:
            List of validation results
        """
        keys = self.detector.detect_from_text(text)

        # Filter duplicates
        new_keys = []
        for key in keys:
            if key.fingerprint not in self.seen_fingerprints:
                self.seen_fingerprints.add(key.fingerprint)
                new_keys.append(key)
                self.detected_keys.append(key)

        if new_keys:
            return await self.validate_keys(new_keys)

        return []

    async def validate_from_json(self, json_path: str) -> list[ValidationResult]:
        """
        Detect and validate keys from JSON scan results.

        Args:
            json_path: Path to JSON file

        Returns:
            List of validation results
        """
        keys = self.detector.detect_from_json(json_path)

        # Show thinking
        self.thinking.think_scan_start(json_path, len(keys))

        # Filter duplicates and show found keys
        new_keys = []
        for key in keys:
            if key.fingerprint not in self.seen_fingerprints:
                self.seen_fingerprints.add(key.fingerprint)
                new_keys.append(key)
                self.detected_keys.append(key)
                self.thinking.think_key_found(key)

        if new_keys:
            return await self.validate_keys(new_keys)

        return []

    async def scan_s3_buckets(
        self,
        bucket_names: list[str],
    ) -> list[dict]:
        """
        Scan S3 buckets for public access.

        Args:
            bucket_names: List of bucket names to scan

        Returns:
            List of scan results
        """
        if not self.config.scan_s3_buckets:
            return []

        results = await self.s3_scanner.scan_multiple(bucket_names)
        return [r.to_dict() for r in results]

    def get_summary(self) -> dict:
        """Get validation summary."""
        valid_results = [r for r in self.validation_results if r.is_valid]

        by_risk = {
            RiskLevel.CRITICAL: 0,
            RiskLevel.HIGH: 0,
            RiskLevel.MEDIUM: 0,
            RiskLevel.LOW: 0,
            RiskLevel.INFO: 0,
        }

        for result in valid_results:
            by_risk[result.risk_level] = by_risk.get(result.risk_level, 0) + 1

        # Show batch summary
        self.thinking.think_batch_summary(
            total_keys=len(self.detected_keys),
            valid_keys=len(valid_results),
            by_risk=by_risk,
        )

        return {
            "total_detected": len(self.detected_keys),
            "total_validated": len(self.validation_results),
            "valid_keys": len(valid_results),
            "invalid_keys": len(self.validation_results) - len(valid_results),
            "by_risk_level": {k.value: v for k, v in by_risk.items()},
            "by_key_type": self._count_by_type(valid_results),
            "attack_vectors": self._collect_attack_vectors(valid_results),
        }

    def _count_by_type(self, results: list[ValidationResult]) -> dict[str, int]:
        """Count results by key type."""
        counts = {}
        for result in results:
            key_type = result.key_info.key_type.value
            counts[key_type] = counts.get(key_type, 0) + 1
        return counts

    def _collect_attack_vectors(
        self,
        results: list[ValidationResult],
    ) -> list[str]:
        """Collect unique attack vectors from results."""
        vectors = set()
        for result in results:
            if result.attack_vectors:
                vectors.update(result.attack_vectors)
        return list(vectors)

    def get_results_for_report(self) -> list[dict]:
        """Get validation results formatted for reporting."""
        return [
            result.to_finding_dict()
            for result in self.validation_results
            if result.is_valid
        ]

    def reset(self) -> None:
        """Reset bridge state."""
        self.detected_keys.clear()
        self.validation_results.clear()
        self.seen_fingerprints.clear()


# Convenience function for quick validation
async def validate_key(key_value: str, key_type: Optional[str] = None) -> dict:
    """
    Quick validation of a single API key.

    Args:
        key_value: The API key value
        key_type: Optional key type hint

    Returns:
        Validation result dictionary
    """
    bridge = KeyValidationBridge(
        config=KeyValidationConfig(show_ai_thinking=False)
    )

    results = await bridge.validate_from_text(key_value)

    if results:
        return results[0].to_finding_dict()

    return {"error": "No valid key detected", "key_value": key_value[:20] + "..."}
