"""AWS error handling utilities for alice-cli.

Provides user-friendly error messages for AWS service errors with:
- Error code extraction and classification
- Context-specific hints for resolution
- Structured error information for logging
- Preservation of exception chain for debugging
"""

from __future__ import annotations

from typing import Any

from botocore.exceptions import BotoCoreError, ClientError

from alice_cli.errors import AliceCLIError, AuthError, LockerError, SecretError


class AWSErrorInfo:
    """Structured information extracted from AWS exceptions."""

    def __init__(
        self,
        *,
        error_code: str,
        message: str,
        request_id: str | None = None,
        status_code: int | None = None,
        service: str | None = None,
        operation: str | None = None,
    ) -> None:
        self.error_code = error_code
        self.message = message
        self.request_id = request_id
        self.status_code = status_code
        self.service = service
        self.operation = operation

    def __str__(self) -> str:
        """Format for display to users."""
        parts = [f"{self.error_code}: {self.message}"]
        if self.request_id:
            parts.append(f"(Request ID: {self.request_id})")
        return " ".join(parts)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dict for structured logging."""
        return {
            "error_code": self.error_code,
            "message": self.message,
            "request_id": self.request_id,
            "status_code": self.status_code,
            "service": self.service,
            "operation": self.operation,
        }


def extract_aws_error(exc: ClientError | BotoCoreError) -> AWSErrorInfo:
    """Extract structured error information from AWS exception.

    Args:
        exc: boto3 ClientError or BotoCoreError exception

    Returns:
        AWSErrorInfo with error code, message, request ID, etc.
    """
    if isinstance(exc, ClientError):
        error_response = exc.response.get("Error", {})
        metadata: dict[str, Any] = exc.response.get("ResponseMetadata", {})  # type: ignore[assignment]
        return AWSErrorInfo(
            error_code=error_response.get("Code", "UnknownError"),
            message=error_response.get("Message", str(exc)),
            request_id=str(metadata["RequestId"]) if "RequestId" in metadata else None,
            status_code=int(metadata["HTTPStatusCode"]) if "HTTPStatusCode" in metadata else None,
            service=str(metadata["ServiceName"]) if "ServiceName" in metadata else None,
            operation=exc.operation_name,
        )
    else:
        # BotoCoreError doesn't have structured error info
        return AWSErrorInfo(
            error_code="BotoCoreError",
            message=str(exc),
        )


def get_error_hint(error_code: str, service: str | None = None) -> str | None:
    """Get user-friendly hint for common AWS error codes.

    Args:
        error_code: AWS error code (e.g., "AccessDeniedException")
        service: AWS service name (e.g., "bedrock-runtime")

    Returns:
        Helpful hint string or None if no specific hint available
    """
    # Service-specific hints (checked first for more specific guidance)
    if service:
        if service == "bedrock-runtime" and "AccessDenied" in error_code:
            return "You may need to request model access in the AWS Bedrock console."
        elif service == "secretsmanager" and "ResourceNotFound" in error_code:
            return "The secret may not exist or you may not have GetSecretValue permission."
        elif service == "s3" and "AccessDenied" in error_code:
            return "Check your S3 bucket permissions and bucket policy."

    # General hints (fallback)
    hints = {
        # Authentication/Authorization
        "ExpiredToken": "Your AWS session has expired. Run: aws sso login --profile <profile>",
        "ExpiredTokenException": "Your AWS session has expired. Run: aws sso login",
        "AccessDeniedException": "Your IAM role lacks permission. Contact your AWS administrator.",
        "UnauthorizedException": "Your credentials are not authorized for this operation.",
        "InvalidClientTokenId": "Your AWS access key ID is invalid or has been deleted.",
        "SignatureDoesNotMatch": "Your AWS secret key may be incorrect.",
        # Throttling
        "ThrottlingException": "AWS is rate-limiting your requests. Wait a moment and try again.",
        "RequestLimitExceeded": "You've exceeded AWS request limits. Wait before retrying.",
        "TooManyRequestsException": "Too many requests. The CLI will retry automatically.",
        # Resource issues
        "ResourceNotFoundException": "The requested resource does not exist or is inaccessible.",
        "NoSuchKey": "The S3 object does not exist.",
        "NoSuchBucket": "The S3 bucket does not exist or you don't have access.",
        # Service issues
        "ServiceUnavailable": "AWS service is unavailable. The CLI will retry automatically.",
        "InternalError": "AWS experienced an internal error. Try again in a few moments.",
        "InternalServerError": "AWS service error. This is usually temporary.",
        # Bedrock-specific
        "ValidationException": "Request validation failed. Check your model ID and parameters.",
        "ModelNotReadyException": "The model is not ready. Try a different model or wait.",
        "ModelStreamErrorException": "Model streaming failed. Try again without streaming.",
        # Secrets Manager
        "InvalidRequestException": "Invalid request format. Check your parameters.",
        "DecryptionFailure": "Failed to decrypt secret. Check KMS permissions.",
    }

    return hints.get(error_code)


def handle_bedrock_error(exc: ClientError | BotoCoreError, *, model_id: str) -> AliceCLIError:
    """Convert AWS Bedrock error to user-friendly AliceCLIError.

    Args:
        exc: boto3 exception from Bedrock API call
        model_id: Model ID that was being invoked

    Returns:
        AliceCLIError with user-friendly message and hints
    """
    error = extract_aws_error(exc)
    hint = get_error_hint(error.error_code, service="bedrock-runtime")

    message_parts = [f"Bedrock API error for model '{model_id}': {error.message}"]
    if hint:
        message_parts.append(f"\nHint: {hint}")
    if error.request_id:
        message_parts.append(f"\nRequest ID: {error.request_id}")

    return AliceCLIError("\n".join(message_parts))


def handle_secrets_error(exc: ClientError | BotoCoreError, *, secret_path: str) -> SecretError:
    """Convert AWS Secrets Manager error to user-friendly SecretError.

    Args:
        exc: boto3 exception from Secrets Manager API call
        secret_path: Secret path that was being accessed

    Returns:
        SecretError with user-friendly message and hints
    """
    error = extract_aws_error(exc)
    hint = get_error_hint(error.error_code, service="secretsmanager")

    message_parts = [f"Failed to retrieve secret '{secret_path}': {error.message}"]
    if hint:
        message_parts.append(f"\nHint: {hint}")
    if error.request_id:
        message_parts.append(f"\nRequest ID: {error.request_id}")

    return SecretError("\n".join(message_parts))


def handle_s3_error(
    exc: ClientError | BotoCoreError, *, bucket: str, key: str | None = None
) -> LockerError:
    """Convert AWS S3 error to user-friendly LockerError.

    Args:
        exc: boto3 exception from S3 API call
        bucket: S3 bucket name
        key: Optional S3 key

    Returns:
        LockerError with user-friendly message and hints
    """
    error = extract_aws_error(exc)
    hint = get_error_hint(error.error_code, service="s3")

    location = f"s3://{bucket}/{key}" if key else f"s3://{bucket}"
    message_parts = [f"S3 operation failed for {location}: {error.message}"]
    if hint:
        message_parts.append(f"\nHint: {hint}")
    if error.request_id:
        message_parts.append(f"\nRequest ID: {error.request_id}")

    return LockerError("\n".join(message_parts))


def handle_sts_error(exc: ClientError | BotoCoreError, *, profile: str) -> AuthError:
    """Convert AWS STS error to user-friendly AuthError.

    Args:
        exc: boto3 exception from STS API call
        profile: AWS profile name

    Returns:
        AuthError with user-friendly message and hints
    """
    error = extract_aws_error(exc)
    hint = get_error_hint(error.error_code, service="sts")

    message_parts = [f"Authentication failed for profile '{profile}': {error.message}"]
    if hint:
        message_parts.append(f"\nHint: {hint}")
    else:
        # Default hint for STS errors
        message_parts.append(f"\nHint: Run: aws sso login --profile {profile}")
    if error.request_id:
        message_parts.append(f"\nRequest ID: {error.request_id}")

    return AuthError("\n".join(message_parts))
