"""Tests for error classification utilities."""

from http import HTTPStatus
from unittest.mock import MagicMock

import pytest

from oro_sdk.error_utils import (
    ALL_ERROR_TYPES,
    ErrorCategory,
    classify_error,
    classify_status,
    get_error_code,
    get_error_detail,
    is_error,
    is_transient,
    is_transient_status,
)
from oro_sdk.models import (
    AgentNotFoundError,
    AtCapacityError,
    CooldownActiveError,
    HTTPValidationError,
    RateLimitExceededError,
    ValidatorNotFoundError,
)
from oro_sdk.types import UNSET, Response


def _make_response(status_code: int, parsed: object = None) -> Response:
    """Create a Response object with the given status code."""
    return Response(
        status_code=HTTPStatus(status_code),
        content=b"",
        headers={},
        parsed=parsed,
    )


class TestClassifyStatus:
    """Tests for classify_status."""

    def test_network_for_zero(self):
        assert classify_status(0) == ErrorCategory.NETWORK

    def test_auth_for_401(self):
        assert classify_status(401) == ErrorCategory.AUTH

    def test_auth_for_403(self):
        assert classify_status(403) == ErrorCategory.AUTH

    def test_not_found_for_404(self):
        assert classify_status(404) == ErrorCategory.NOT_FOUND

    def test_conflict_for_409(self):
        assert classify_status(409) == ErrorCategory.CONFLICT

    def test_validation_for_422(self):
        assert classify_status(422) == ErrorCategory.VALIDATION

    def test_rate_limited_for_429(self):
        assert classify_status(429) == ErrorCategory.RATE_LIMITED

    def test_server_for_500(self):
        assert classify_status(500) == ErrorCategory.SERVER

    def test_server_for_502(self):
        assert classify_status(502) == ErrorCategory.SERVER

    def test_server_for_503(self):
        assert classify_status(503) == ErrorCategory.SERVER

    def test_validation_for_other_4xx(self):
        assert classify_status(400) == ErrorCategory.VALIDATION
        assert classify_status(405) == ErrorCategory.VALIDATION
        assert classify_status(415) == ErrorCategory.VALIDATION

    def test_unknown_for_2xx(self):
        assert classify_status(200) == ErrorCategory.UNKNOWN

    def test_unknown_for_3xx(self):
        assert classify_status(301) == ErrorCategory.UNKNOWN


class TestClassifyError:
    """Tests for classify_error with Response objects."""

    def test_auth_response(self):
        resp = _make_response(401)
        assert classify_error(resp) == ErrorCategory.AUTH

    def test_not_found_response(self):
        resp = _make_response(404)
        assert classify_error(resp) == ErrorCategory.NOT_FOUND

    def test_server_response(self):
        resp = _make_response(500)
        assert classify_error(resp) == ErrorCategory.SERVER

    def test_rate_limited_response(self):
        resp = _make_response(429)
        assert classify_error(resp) == ErrorCategory.RATE_LIMITED


class TestIsTransientStatus:
    """Tests for is_transient_status."""

    def test_true_for_429(self):
        assert is_transient_status(429) is True

    def test_true_for_500(self):
        assert is_transient_status(500) is True

    def test_true_for_502(self):
        assert is_transient_status(502) is True

    def test_true_for_503(self):
        assert is_transient_status(503) is True

    def test_true_for_zero(self):
        assert is_transient_status(0) is True

    def test_false_for_400(self):
        assert is_transient_status(400) is False

    def test_false_for_401(self):
        assert is_transient_status(401) is False

    def test_false_for_404(self):
        assert is_transient_status(404) is False

    def test_false_for_200(self):
        assert is_transient_status(200) is False


class TestIsTransient:
    """Tests for is_transient with Response objects."""

    def test_true_for_500_response(self):
        assert is_transient(_make_response(500)) is True

    def test_true_for_429_response(self):
        assert is_transient(_make_response(429)) is True

    def test_false_for_404_response(self):
        assert is_transient(_make_response(404)) is False


class TestGetErrorDetail:
    """Tests for get_error_detail."""

    def test_extracts_string_detail(self):
        error = AtCapacityError(detail="At capacity")
        assert get_error_detail(error) == "At capacity"

    def test_returns_none_for_none(self):
        assert get_error_detail(None) is None

    def test_returns_none_for_validation_error_list_detail(self):
        error = HTTPValidationError(detail=UNSET)
        assert get_error_detail(error) is None

    def test_returns_none_for_no_detail_attr(self):
        assert get_error_detail(object()) is None

    def test_extracts_from_real_model(self):
        error = AgentNotFoundError(detail="Agent xyz not found")
        assert get_error_detail(error) == "Agent xyz not found"


class TestGetErrorCode:
    """Tests for get_error_code."""

    def test_extracts_code_when_present(self):
        error = AtCapacityError(detail="At capacity", error_code="AT_CAPACITY")
        assert get_error_code(error) == "AT_CAPACITY"

    def test_returns_none_when_unset(self):
        error = AtCapacityError(detail="At capacity", error_code=UNSET)
        assert get_error_code(error) is None

    def test_returns_none_for_none(self):
        assert get_error_code(None) is None

    def test_returns_none_for_no_attr(self):
        assert get_error_code(object()) is None

    def test_extracts_from_different_models(self):
        error = CooldownActiveError(detail="Cooldown", error_code="COOLDOWN_ACTIVE", remaining_seconds=30)
        assert get_error_code(error) == "COOLDOWN_ACTIVE"


class TestIsError:
    """Tests for is_error."""

    def test_true_for_known_error_types(self):
        assert is_error(AtCapacityError(detail="error")) is True
        assert is_error(AgentNotFoundError(detail="not found")) is True
        assert is_error(RateLimitExceededError(detail="rate limited")) is True
        assert is_error(ValidatorNotFoundError(detail="not found")) is True

    def test_false_for_non_error_types(self):
        assert is_error("string") is False
        assert is_error(42) is False
        assert is_error(None) is False
        assert is_error({}) is False


class TestAllErrorTypes:
    """Tests for ALL_ERROR_TYPES tuple."""

    def test_contains_expected_types(self):
        assert len(ALL_ERROR_TYPES) >= 25

    def test_all_are_classes(self):
        for error_type in ALL_ERROR_TYPES:
            assert isinstance(error_type, type)

    def test_includes_known_types(self):
        assert AtCapacityError in ALL_ERROR_TYPES
        assert HTTPValidationError in ALL_ERROR_TYPES
        assert AgentNotFoundError in ALL_ERROR_TYPES
        assert RateLimitExceededError in ALL_ERROR_TYPES


class TestErrorCategoryEnum:
    """Tests for ErrorCategory enum."""

    def test_is_string_enum(self):
        assert isinstance(ErrorCategory.NETWORK, str)
        assert ErrorCategory.NETWORK == "NETWORK"

    def test_all_categories_exist(self):
        categories = {"NETWORK", "AUTH", "NOT_FOUND", "VALIDATION", "CONFLICT", "RATE_LIMITED", "SERVER", "UNKNOWN"}
        assert {c.value for c in ErrorCategory} == categories
