"""
AIPTX SendGrid Key Validator
============================

Validates SendGrid API keys.

Supports:
- Full access API keys
- Restricted API keys
"""

from __future__ import annotations

import re
import time
import logging
from typing import Any

import httpx

from ..base import (
    APIKeyInfo,
    ValidationResult,
    BaseKeyValidator,
    KeyType,
    RiskLevel,
)

logger = logging.getLogger(__name__)


class SendGridKeyValidator(BaseKeyValidator):
    """
    Validate SendGrid API keys and enumerate permissions.

    Uses SendGrid REST API to validate keys and discover:
    - User profile
    - API key scopes
    - Accessible resources (templates, contacts, etc.)
    """

    API_BASE = "https://api.sendgrid.com/v3"

    def get_key_type(self) -> KeyType:
        return KeyType.SENDGRID

    def get_key_patterns(self) -> dict[str, re.Pattern]:
        return {
            "sendgrid_api_key": re.compile(r'SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}'),
        }

    async def validate(self, key_info: APIKeyInfo) -> ValidationResult:
        """
        Validate SendGrid key against API.

        Calls GET /user/profile to validate and retrieve identity.
        """
        start_time = time.time()

        try:
            async with httpx.AsyncClient(timeout=self.timeout) as client:
                headers = {
                    "Authorization": f"Bearer {key_info.key_value}",
                    "Content-Type": "application/json",
                }

                # Get user profile
                response = await client.get(
                    f"{self.API_BASE}/user/profile",
                    headers=headers,
                )

                duration_ms = (time.time() - start_time) * 1000

                if response.status_code == 200:
                    profile_data = response.json()

                    # Build identity info
                    identity_info = {
                        "email": profile_data.get("email"),
                        "first_name": profile_data.get("first_name"),
                        "last_name": profile_data.get("last_name"),
                        "company": profile_data.get("company"),
                        "phone": profile_data.get("phone"),
                        "address": profile_data.get("address"),
                        "city": profile_data.get("city"),
                        "country": profile_data.get("country"),
                    }

                    # Get API key scopes and enumerate access
                    scopes, permissions, resources = await self._enumerate_access(
                        client, headers
                    )

                    result = ValidationResult(
                        key_info=key_info,
                        is_valid=True,
                        validation_method="GET /user/profile",
                        validation_endpoint=f"{self.API_BASE}/user/profile",
                        identity_info=identity_info,
                        scopes=scopes,
                        permissions=permissions,
                        resources_accessible=resources,
                        raw_response=profile_data,
                        validated_at=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
                        validation_duration_ms=duration_ms,
                    )

                    # Assess risk
                    result.risk_level = self._assess_sendgrid_risk(scopes, permissions)
                    result.exploitation_potential = self._get_exploitation_potential(result)
                    result.attack_vectors = self._get_attack_vectors(scopes, permissions)

                    return result

                elif response.status_code == 401:
                    return self._create_error_result(
                        key_info,
                        error="Invalid API key",
                        error_code="401_UNAUTHORIZED",
                        duration_ms=duration_ms,
                    )
                elif response.status_code == 403:
                    # Key valid but lacks profile access, try alternative
                    return await self._validate_limited_key(
                        client, key_info, headers, start_time
                    )
                else:
                    return self._create_error_result(
                        key_info,
                        error=f"Unexpected status: {response.status_code}",
                        error_code=str(response.status_code),
                        duration_ms=duration_ms,
                    )

        except httpx.TimeoutException:
            duration_ms = (time.time() - start_time) * 1000
            return self._create_error_result(
                key_info,
                error="Request timeout",
                error_code="TIMEOUT",
                duration_ms=duration_ms,
            )
        except Exception as e:
            duration_ms = (time.time() - start_time) * 1000
            logger.error(f"SendGrid validation error: {e}")
            return self._create_error_result(
                key_info,
                error=str(e),
                error_code="ERROR",
                duration_ms=duration_ms,
            )

    async def _validate_limited_key(
        self,
        client: httpx.AsyncClient,
        key_info: APIKeyInfo,
        headers: dict,
        start_time: float,
    ) -> ValidationResult:
        """Validate key with limited permissions."""
        duration_ms = (time.time() - start_time) * 1000

        # Try to get scopes from API keys endpoint
        response = await client.get(
            f"{self.API_BASE}/scopes",
            headers=headers,
        )

        if response.status_code == 200:
            scopes_data = response.json()
            scopes = scopes_data.get("scopes", [])

            return ValidationResult(
                key_info=key_info,
                is_valid=True,
                validation_method="GET /scopes",
                validation_endpoint=f"{self.API_BASE}/scopes",
                identity_info={"key_type": "restricted"},
                scopes=scopes,
                permissions=self._scopes_to_permissions(scopes),
                resources_accessible=[],
                validated_at=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
                validation_duration_ms=duration_ms,
                risk_level=self._assess_sendgrid_risk(scopes, []),
            )

        return self._create_error_result(
            key_info,
            error="Could not validate key permissions",
            error_code="LIMITED_ACCESS",
            duration_ms=duration_ms,
        )

    async def _enumerate_access(
        self,
        client: httpx.AsyncClient,
        headers: dict,
    ) -> tuple[list[str], list[str], list[str]]:
        """Enumerate SendGrid access and permissions."""
        scopes = []
        permissions = []
        resources = []

        # Get available scopes
        try:
            response = await client.get(
                f"{self.API_BASE}/scopes",
                headers=headers,
            )
            if response.status_code == 200:
                data = response.json()
                scopes = data.get("scopes", [])
                permissions = self._scopes_to_permissions(scopes)
        except Exception as e:
            logger.debug(f"Failed to get scopes: {e}")

        # Check templates
        try:
            response = await client.get(
                f"{self.API_BASE}/templates",
                headers=headers,
            )
            if response.status_code == 200:
                data = response.json()
                templates = data.get("templates", [])
                if templates:
                    resources.append(f"templates:{len(templates)}")
        except Exception:
            pass

        # Check contacts
        try:
            response = await client.get(
                f"{self.API_BASE}/marketing/contacts/count",
                headers=headers,
            )
            if response.status_code == 200:
                data = response.json()
                count = data.get("contact_count", 0)
                if count:
                    resources.append(f"contacts:{count}")
        except Exception:
            pass

        # Check sender identities
        try:
            response = await client.get(
                f"{self.API_BASE}/verified_senders",
                headers=headers,
            )
            if response.status_code == 200:
                data = response.json()
                senders = data.get("results", [])
                if senders:
                    sender_emails = [s.get("from_email") for s in senders if s.get("from_email")]
                    resources.append(f"verified_senders:{len(senders)}")
                    if sender_emails:
                        resources.extend([f"sender:{e}" for e in sender_emails[:3]])
        except Exception:
            pass

        # Check suppressions (bounces, spam reports)
        try:
            response = await client.get(
                f"{self.API_BASE}/suppression/bounces",
                headers=headers,
            )
            if response.status_code == 200:
                data = response.json()
                if data:
                    resources.append(f"bounces:{len(data)}")
        except Exception:
            pass

        # Check stats
        try:
            response = await client.get(
                f"{self.API_BASE}/stats",
                headers=headers,
            )
            if response.status_code == 200:
                resources.append("email_stats")
        except Exception:
            pass

        return scopes, permissions, resources

    def _scopes_to_permissions(self, scopes: list[str]) -> list[str]:
        """Convert SendGrid scopes to permission descriptions."""
        scope_descriptions = {
            "mail.send": "Send emails",
            "mail.batch.create": "Create mail batches",
            "mail.batch.delete": "Delete mail batches",
            "templates.read": "Read email templates",
            "templates.create": "Create email templates",
            "templates.delete": "Delete email templates",
            "marketing.contacts.read": "Read marketing contacts",
            "marketing.contacts.write": "Write marketing contacts",
            "marketing.lists.read": "Read mailing lists",
            "marketing.lists.write": "Write mailing lists",
            "sender_verification.read": "Read verified senders",
            "sender_verification.create": "Create verified senders",
            "suppressions.read": "Read suppression lists",
            "suppressions.write": "Write suppression lists",
            "stats.read": "Read email statistics",
            "api_keys.read": "Read API keys",
            "api_keys.create": "Create API keys",
            "api_keys.delete": "Delete API keys",
            "user.profile.read": "Read user profile",
            "user.profile.update": "Update user profile",
            "2fa.read": "Read 2FA settings",
            "2fa.update": "Update 2FA settings",
        }

        permissions = []
        for scope in scopes:
            desc = scope_descriptions.get(scope)
            if desc:
                permissions.append(desc)
            elif "." in scope:
                # Parse unknown scopes
                parts = scope.split(".")
                permissions.append(f"{parts[0].title()} {parts[-1]}")

        return permissions

    def _assess_sendgrid_risk(
        self,
        scopes: list[str],
        permissions: list[str],
    ) -> RiskLevel:
        """Assess risk level based on key capabilities."""
        # Critical: Can send mail and access contacts
        if "mail.send" in scopes and any("contacts" in s for s in scopes):
            return RiskLevel.CRITICAL

        # High: Can send mail
        if "mail.send" in scopes:
            return RiskLevel.HIGH

        # Medium: Can read contacts or templates
        if any("contacts" in s or "templates" in s for s in scopes):
            return RiskLevel.MEDIUM

        # Low: Read-only access
        if scopes:
            return RiskLevel.LOW

        return RiskLevel.INFO

    def _get_exploitation_potential(self, result: ValidationResult) -> str:
        """Get exploitation potential description."""
        if result.risk_level == RiskLevel.CRITICAL:
            return "Critical - Can send emails and access contact lists"
        elif result.risk_level == RiskLevel.HIGH:
            return "High - Can send emails from verified domains"
        elif result.risk_level == RiskLevel.MEDIUM:
            return "Medium - Can access email templates and contacts"
        elif result.risk_level == RiskLevel.LOW:
            return "Low - Read-only access to email resources"
        return "Minimal - Very limited permissions"

    def _get_attack_vectors(
        self,
        scopes: list[str],
        permissions: list[str],
    ) -> list[str]:
        """Identify potential attack vectors."""
        vectors = []

        if "mail.send" in scopes:
            vectors.append("Send phishing emails from verified domain")
            vectors.append("Spam campaigns using existing reputation")

        if any("contacts" in s for s in scopes):
            vectors.append("Exfiltrate email contact lists")
            vectors.append("Harvest email addresses for targeted attacks")

        if any("templates" in s for s in scopes):
            vectors.append("Access/modify email templates")
            vectors.append("Inject malicious content into templates")

        if any("api_keys" in s for s in scopes):
            vectors.append("Create new API keys for persistence")
            vectors.append("Elevate privileges via new keys")

        if any("sender" in s for s in scopes):
            vectors.append("View verified sender identities")
            vectors.append("Impersonate organization in emails")

        return vectors
