"""
Tests for arp_sdk.errors module.

Covers the error class hierarchy, the from_response() factory function,
error codes, and edge cases with various body formats.
"""

import pytest

from arp_sdk.errors import (
    ARPError,
    AuthError,
    ConflictError,
    NotFoundError,
    RateLimitError,
    ServerError,
    TimeoutError,
    ValidationError,
    from_response,
)


# ──────────────────────────────────────────────
# Error hierarchy
# ──────────────────────────────────────────────


class TestErrorHierarchy:
    """All concrete error types must inherit from ARPError."""

    @pytest.mark.parametrize(
        "cls",
        [AuthError, NotFoundError, ValidationError, RateLimitError, ConflictError, ServerError, TimeoutError],
    )
    def test_all_errors_inherit_from_arp_error(self, cls):
        assert issubclass(cls, ARPError)

    def test_arp_error_inherits_from_exception(self):
        assert issubclass(ARPError, Exception)


# ──────────────────────────────────────────────
# Error codes
# ──────────────────────────────────────────────


class TestErrorCodes:
    """Each error subclass must set the expected code string."""

    def test_auth_error_code(self):
        err = AuthError("unauthorized")
        assert err.code == "AUTH_ERROR"

    def test_not_found_error_code(self):
        err = NotFoundError("missing")
        assert err.code == "NOT_FOUND"

    def test_validation_error_code(self):
        err = ValidationError("bad input")
        assert err.code == "VALIDATION_ERROR"

    def test_rate_limit_error_code(self):
        err = RateLimitError("slow down")
        assert err.code == "RATE_LIMITED"

    def test_conflict_error_code(self):
        err = ConflictError("already exists")
        assert err.code == "CONFLICT"

    def test_server_error_code(self):
        err = ServerError("internal", status_code=500)
        assert err.code == "SERVER_ERROR"

    def test_timeout_error_code(self):
        err = TimeoutError()
        assert err.code == "TIMEOUT"


# ──────────────────────────────────────────────
# Error attributes
# ──────────────────────────────────────────────


class TestErrorAttributes:
    """Verify that extra attributes are stored correctly."""

    def test_validation_error_field_errors(self):
        fe = {"email": ["invalid format"], "name": ["too short"]}
        err = ValidationError("bad input", field_errors=fe)
        assert err.field_errors == fe

    def test_validation_error_field_errors_default_empty(self):
        err = ValidationError("bad input")
        assert err.field_errors == {}

    def test_rate_limit_error_retry_after(self):
        err = RateLimitError("slow down", retry_after=30.0)
        assert err.retry_after == 30.0

    def test_rate_limit_error_retry_after_default_none(self):
        err = RateLimitError("slow down")
        assert err.retry_after is None

    def test_server_error_status_code(self):
        err = ServerError("oops", status_code=502)
        assert err.status_code == 502

    def test_timeout_error_default_message(self):
        err = TimeoutError()
        assert err.message == "Request timed out"

    def test_arp_error_details_default_empty(self):
        err = ARPError("CODE", "msg")
        assert err.details == {}

    def test_arp_error_details_stored(self):
        d = {"extra": "info"}
        err = ARPError("CODE", "msg", details=d)
        assert err.details == d

    def test_arp_error_repr(self):
        err = ARPError("CODE", "msg")
        assert repr(err) == "ARPError(code='CODE', message='msg')"

    def test_auth_error_repr(self):
        err = AuthError("denied")
        assert repr(err) == "AuthError(code='AUTH_ERROR', message='denied')"


# ──────────────────────────────────────────────
# from_response() factory function
# ──────────────────────────────────────────────


class TestFromResponse:
    """The from_response() factory must return the correct error subclass."""

    def test_400_returns_validation_error(self):
        body = {"message": "Invalid input", "fieldErrors": {"name": ["required"]}}
        err = from_response(400, body)
        assert isinstance(err, ValidationError)
        assert err.message == "Invalid input"
        assert err.field_errors == {"name": ["required"]}

    def test_422_returns_validation_error(self):
        body = {"message": "Unprocessable entity"}
        err = from_response(422, body)
        assert isinstance(err, ValidationError)
        assert err.message == "Unprocessable entity"

    def test_401_returns_auth_error(self):
        body = {"message": "Unauthorized"}
        err = from_response(401, body)
        assert isinstance(err, AuthError)
        assert err.message == "Unauthorized"
        assert err.code == "AUTH_ERROR"

    def test_403_returns_auth_error(self):
        body = {"message": "Forbidden"}
        err = from_response(403, body)
        assert isinstance(err, AuthError)
        assert err.message == "Forbidden"

    def test_404_returns_not_found_error(self):
        body = {"message": "Agent not found"}
        err = from_response(404, body)
        assert isinstance(err, NotFoundError)
        assert err.message == "Agent not found"
        assert err.code == "NOT_FOUND"

    def test_409_returns_conflict_error(self):
        body = {"message": "Agent already exists"}
        err = from_response(409, body)
        assert isinstance(err, ConflictError)
        assert err.message == "Agent already exists"
        assert err.code == "CONFLICT"

    def test_429_returns_rate_limit_error(self):
        body = {"message": "Rate limit exceeded", "retryAfter": 60}
        err = from_response(429, body)
        assert isinstance(err, RateLimitError)
        assert err.message == "Rate limit exceeded"
        assert err.retry_after == 60
        assert err.code == "RATE_LIMITED"

    def test_429_without_retry_after(self):
        body = {"message": "Too many requests"}
        err = from_response(429, body)
        assert isinstance(err, RateLimitError)
        assert err.retry_after is None

    def test_500_returns_server_error(self):
        body = {"message": "Internal server error"}
        err = from_response(500, body)
        assert isinstance(err, ServerError)
        assert err.message == "Internal server error"
        assert err.status_code == 500
        assert err.code == "SERVER_ERROR"

    def test_502_returns_server_error(self):
        body = {"message": "Bad gateway"}
        err = from_response(502, body)
        assert isinstance(err, ServerError)
        assert err.status_code == 502

    def test_503_returns_server_error(self):
        body = {"message": "Service unavailable"}
        err = from_response(503, body)
        assert isinstance(err, ServerError)
        assert err.status_code == 503

    def test_string_body_parsed_as_json(self):
        body = '{"message": "Unauthorized", "code": "AUTH_ERROR"}'
        err = from_response(401, body)
        assert isinstance(err, AuthError)
        assert err.message == "Unauthorized"

    def test_string_body_with_error_key(self):
        body = '{"error": "Something went wrong"}'
        err = from_response(500, body)
        assert isinstance(err, ServerError)
        assert err.message == "Something went wrong"

    def test_string_body_invalid_json(self):
        body = "not json at all"
        err = from_response(500, body)
        assert isinstance(err, ServerError)
        # Falls back to default message since JSON parse fails → parsed=None
        assert err.message == "HTTP Error 500"

    def test_none_body_uses_default_message(self):
        err = from_response(404, None)
        assert isinstance(err, NotFoundError)
        assert err.message == "HTTP Error 404"

    def test_none_body_empty_details(self):
        err = from_response(500, None)
        assert isinstance(err, ServerError)
        assert err.details == {}

    def test_unknown_status_returns_base_arp_error(self):
        body = {"message": "Teapot"}
        err = from_response(418, body)
        assert type(err) is ARPError
        assert err.code == "UNKNOWN_ERROR"
        assert err.message == "Teapot"

    def test_dict_body_details_preserved(self):
        body = {"message": "Not found", "resource": "agent", "id": "abc"}
        err = from_response(404, body)
        assert err.details == body

    def test_400_with_no_field_errors_key(self):
        body = {"message": "Bad request"}
        err = from_response(400, body)
        assert isinstance(err, ValidationError)
        assert err.field_errors == {}

    def test_from_response_is_catchable_as_arp_error(self):
        err = from_response(401, {"message": "nope"})
        with pytest.raises(ARPError):
            raise err

    def test_from_response_is_catchable_as_specific_type(self):
        err = from_response(429, {"message": "slow"})
        with pytest.raises(RateLimitError):
            raise err
