from __future__ import annotations

import grpc
import pytest

from kyrodb.errors import (
    AuthenticationError,
    DeadlineExceededError,
    InternalServerError,
    InvalidArgumentError,
    KyroDBError,
    NotFoundError,
    PermissionDeniedError,
    QuotaExceededError,
    ServiceUnavailableError,
    map_rpc_error,
)


class _FakeRpcError(grpc.RpcError):
    def __init__(self, code: grpc.StatusCode, details: str) -> None:
        super().__init__()
        self._code = code
        self._details = details

    def code(self) -> grpc.StatusCode:
        return self._code

    def details(self) -> str:
        return self._details


def test_map_rpc_error_authentication() -> None:
    err = _FakeRpcError(grpc.StatusCode.UNAUTHENTICATED, "bad key")
    mapped = map_rpc_error(err, operation="Search", target="127.0.0.1:50051")
    assert isinstance(mapped, AuthenticationError)
    assert "Search failed [UNAUTHENTICATED]" in str(mapped)


def test_map_rpc_error_not_found() -> None:
    err = _FakeRpcError(grpc.StatusCode.NOT_FOUND, "missing")
    mapped = map_rpc_error(err, operation="Query", target="127.0.0.1:50051")
    assert isinstance(mapped, NotFoundError)


def test_map_rpc_error_fallback() -> None:
    err = _FakeRpcError(grpc.StatusCode.UNKNOWN, "unknown")
    mapped = map_rpc_error(err, operation="Metrics", target="127.0.0.1:50051")
    assert isinstance(mapped, KyroDBError)


def test_map_rpc_error_redacts_sensitive_details() -> None:
    err = _FakeRpcError(grpc.StatusCode.UNAUTHENTICATED, "x-api-key: supersecret")
    mapped = map_rpc_error(err, operation="Search", target="127.0.0.1:50051")
    assert isinstance(mapped, AuthenticationError)
    assert "supersecret" not in mapped.details
    assert "<redacted>" in mapped.details


@pytest.mark.parametrize(
    ("code", "expected_type"),
    [
        (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 test_map_rpc_error_all_mapped_codes(
    code: grpc.StatusCode,
    expected_type: type[KyroDBError],
) -> None:
    mapped = map_rpc_error(
        _FakeRpcError(code, "x-api-key: abc"),
        operation="Op",
        target="127.0.0.1:50051",
    )
    assert isinstance(mapped, expected_type)
