"""SDK error types and gRPC status mapping."""

from __future__ import annotations

from dataclasses import dataclass

import grpc

from .auth import redact_sensitive_text


@dataclass(slots=True)
class KyroDBError(Exception):
    operation: str
    code: grpc.StatusCode
    details: str
    target: str

    def __str__(self) -> str:
        return f"{self.operation} failed [{self.code.name}] on {self.target}: {self.details}"


class InvalidArgumentError(KyroDBError):
    pass


class AuthenticationError(KyroDBError):
    pass


class PermissionDeniedError(KyroDBError):
    pass


class NotFoundError(KyroDBError):
    pass


class QuotaExceededError(KyroDBError):
    pass


class DeadlineExceededError(KyroDBError):
    pass


class ServiceUnavailableError(KyroDBError):
    pass


class CircuitOpenError(ServiceUnavailableError):
    pass


class InternalServerError(KyroDBError):
    pass


_ERROR_MAP: dict[grpc.StatusCode, type[KyroDBError]] = {
    grpc.StatusCode.INVALID_ARGUMENT: InvalidArgumentError,
    grpc.StatusCode.UNAUTHENTICATED: AuthenticationError,
    grpc.StatusCode.PERMISSION_DENIED: PermissionDeniedError,
    grpc.StatusCode.NOT_FOUND: NotFoundError,
    grpc.StatusCode.RESOURCE_EXHAUSTED: QuotaExceededError,
    grpc.StatusCode.DEADLINE_EXCEEDED: DeadlineExceededError,
    grpc.StatusCode.UNAVAILABLE: ServiceUnavailableError,
    grpc.StatusCode.INTERNAL: InternalServerError,
    grpc.StatusCode.DATA_LOSS: InternalServerError,
}


def map_rpc_error(error: grpc.RpcError, operation: str, target: str) -> KyroDBError:
    code = error.code() if hasattr(error, "code") else grpc.StatusCode.UNKNOWN
    details = error.details() if hasattr(error, "details") else str(error)
    safe_details = redact_sensitive_text(details or "")
    exc_type = _ERROR_MAP.get(code, KyroDBError)
    return exc_type(operation=operation, code=code, details=safe_details, target=target)
