from __future__ import annotations

from .._compat import optional_import
from .base import ErrorInfo, default_message

RedisExceptions = optional_import("redis", "exceptions")
KafkaErrors = optional_import("kafka", "errors")
ConfluentKafka = optional_import("confluent_kafka")
ConfluentErrorModule = optional_import("confluent_kafka.error")
CeleryExceptions = optional_import("celery", "exceptions")
KombuExceptions = optional_import("kombu", "exceptions")
BilliardExceptions = optional_import("billiard", "exceptions")


def _class(name: str, module: object) -> type[object] | None:
    return getattr(module, name, None) if module else None


def describe_cache_queue_exception(exc: Exception) -> ErrorInfo | None:
    if RedisExceptions:
        if isinstance(exc, tuple(filter(None, (_class("TimeoutError", RedisExceptions),)))):
            return ErrorInfo(504, "cache_timeout", default_message(504), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("AuthenticationError", RedisExceptions), _class("AuthorizationError", RedisExceptions))))):
            return ErrorInfo(401, "cache_auth_failed", default_message(401), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("WatchError", RedisExceptions), _class("ExecAbortError", RedisExceptions), _class("LockError", RedisExceptions), _class("LockNotOwnedError", RedisExceptions))))):
            return ErrorInfo(409, "cache_conflict", default_message(409), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("DataError", RedisExceptions), _class("ResponseError", RedisExceptions))))):
            return ErrorInfo(422, "invalid_cache_operation", default_message(422), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("ConnectionError", RedisExceptions), _class("BusyLoadingError", RedisExceptions), _class("ClusterDownError", RedisExceptions), _class("MaxConnectionsError", RedisExceptions), _class("TryAgainError", RedisExceptions), _class("ReadOnlyError", RedisExceptions))))):
            return ErrorInfo(503, "cache_unavailable", default_message(503), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("RedisError", RedisExceptions),)))):
            return ErrorInfo(503, "cache_error", default_message(503), error_type=exc.__class__.__name__)

    if KafkaErrors:
        if isinstance(exc, tuple(filter(None, (_class("KafkaTimeoutError", KafkaErrors), _class("RequestTimedOutError", KafkaErrors))))):
            return ErrorInfo(504, "queue_timeout", default_message(504), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("NoBrokersAvailable", KafkaErrors), _class("KafkaConnectionError", KafkaErrors), _class("LeaderNotAvailableError", KafkaErrors), _class("NotLeaderForPartitionError", KafkaErrors))))):
            return ErrorInfo(503, "queue_unavailable", default_message(503), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("TopicAuthorizationFailedError", KafkaErrors), _class("GroupAuthorizationFailedError", KafkaErrors), _class("ClusterAuthorizationFailedError", KafkaErrors))))):
            return ErrorInfo(403, "queue_forbidden", default_message(403), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("UnknownTopicOrPartitionError", KafkaErrors),)))):
            return ErrorInfo(404, "queue_resource_not_found", default_message(404), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("InvalidMessageError", KafkaErrors), _class("MessageSizeTooLargeError", KafkaErrors), _class("RecordListTooLargeError", KafkaErrors))))):
            return ErrorInfo(422, "invalid_queue_payload", default_message(422), error_type=exc.__class__.__name__, retryable=False)

    if ConfluentKafka and ConfluentErrorModule:
        confluent_kafka_exception = getattr(ConfluentKafka, "KafkaException", None)
        serialization_types = tuple(filter(None, (getattr(ConfluentErrorModule, "SerializationError", None), getattr(ConfluentErrorModule, "KeySerializationError", None), getattr(ConfluentErrorModule, "ValueSerializationError", None), getattr(ConfluentErrorModule, "KeyDeserializationError", None), getattr(ConfluentErrorModule, "ValueDeserializationError", None))))
        if serialization_types and isinstance(exc, serialization_types):
            return ErrorInfo(422, "invalid_queue_payload", default_message(422), error_type=exc.__class__.__name__, retryable=False)
        if confluent_kafka_exception and isinstance(exc, confluent_kafka_exception):
            kafka_error = exc.args[0] if getattr(exc, "args", None) else None
            code_name = kafka_error.name() if kafka_error and hasattr(kafka_error, "name") else ""
            if "TIMED_OUT" in code_name:
                return ErrorInfo(504, "queue_timeout", default_message(504), error_type=exc.__class__.__name__, retryable=True)
            if "AUTH" in code_name:
                return ErrorInfo(403, "queue_forbidden", default_message(403), error_type=exc.__class__.__name__, retryable=False)
            return ErrorInfo(503, "queue_unavailable", default_message(503), error_type=exc.__class__.__name__, retryable=True)

    if CeleryExceptions:
        if isinstance(exc, tuple(filter(None, (_class("TimeLimitExceeded", CeleryExceptions), _class("SoftTimeLimitExceeded", BilliardExceptions), _class("TimeoutError", CeleryExceptions), _class("TimeLimitExceeded", BilliardExceptions))))):
            return ErrorInfo(504, "task_timeout", default_message(504), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("TaskRevokedError", CeleryExceptions), _class("WorkerLostError", BilliardExceptions))))):
            return ErrorInfo(503, "task_unavailable", default_message(503), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("NotRegistered", CeleryExceptions), _class("QueueNotFound", CeleryExceptions))))):
            return ErrorInfo(404, "task_resource_not_found", default_message(404), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("InvalidTaskError", CeleryExceptions),)))):
            return ErrorInfo(400, "invalid_task", default_message(400), error_type=exc.__class__.__name__, retryable=False)
        if isinstance(exc, tuple(filter(None, (_class("MaxRetriesExceededError", CeleryExceptions),)))):
            return ErrorInfo(503, "task_retries_exhausted", default_message(503), error_type=exc.__class__.__name__, retryable=False)

    if KombuExceptions:
        if isinstance(exc, tuple(filter(None, (_class("TimeoutError", KombuExceptions),)))):
            return ErrorInfo(504, "queue_timeout", default_message(504), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("OperationalError", KombuExceptions), _class("ConnectionError", KombuExceptions), _class("ResourceError", KombuExceptions), _class("LimitExceeded", KombuExceptions), _class("ConnectionLimitExceeded", KombuExceptions), _class("ChannelLimitExceeded", KombuExceptions))))):
            return ErrorInfo(503, "queue_unavailable", default_message(503), error_type=exc.__class__.__name__, retryable=True)
        if isinstance(exc, tuple(filter(None, (_class("SerializationError", KombuExceptions), _class("EncodeError", KombuExceptions), _class("DecodeError", KombuExceptions), _class("ContentDisallowed", KombuExceptions), _class("SerializerNotInstalled", KombuExceptions))))):
            return ErrorInfo(422, "invalid_queue_payload", default_message(422), error_type=exc.__class__.__name__, retryable=False)

    return None
