"""Unit tests for ZyndPay error classes."""

import pytest

from zyndpay.errors import (
    AuthenticationError,
    ConflictError,
    NotFoundError,
    RateLimitError,
    ValidationError,
    ZyndPayError,
)


# -- ZyndPayError base class ---------------------------------------------------


def test_zyndpay_error_stores_all_attributes():
    err = ZyndPayError(
        "something broke",
        status_code=500,
        request_id="req_abc",
        code="INTERNAL",
        raw_body='{"success":false}',
    )
    assert str(err) == "something broke"
    assert err.message == "something broke"
    assert err.status_code == 500
    assert err.request_id == "req_abc"
    assert err.code == "INTERNAL"
    assert err.raw_body == '{"success":false}'


def test_zyndpay_error_defaults_to_none():
    err = ZyndPayError("bare error")
    assert err.status_code is None
    assert err.request_id is None
    assert err.code is None
    assert err.raw_body is None


def test_zyndpay_error_is_exception():
    with pytest.raises(ZyndPayError):
        raise ZyndPayError("boom")


# -- ConflictError -------------------------------------------------------------


def test_conflict_error_code_and_status():
    err = ConflictError("duplicate resource")
    assert err.code == "CONFLICT"
    assert err.status_code == 409
    assert isinstance(err, ZyndPayError)


def test_conflict_error_inherits_from_zyndpay_error():
    err = ConflictError("dup")
    assert isinstance(err, ZyndPayError)
    assert isinstance(err, Exception)


# -- ValidationError -----------------------------------------------------------


def test_validation_error_default_code():
    err = ValidationError("bad input")
    assert err.code == "VALIDATION_ERROR"
    assert err.status_code == 400


def test_validation_error_custom_code():
    err = ValidationError("amount too small", code="AMOUNT_TOO_SMALL")
    assert err.code == "AMOUNT_TOO_SMALL"


def test_validation_error_stores_details():
    details = [{"field": "amount", "message": "must be >= 25"}]
    err = ValidationError("bad input", details=details)
    assert err.details == details
    assert len(err.details) == 1
    assert err.details[0]["field"] == "amount"


def test_validation_error_details_default_none():
    err = ValidationError("bad input")
    assert err.details is None


def test_validation_error_inherits_from_zyndpay_error():
    err = ValidationError("bad")
    assert isinstance(err, ZyndPayError)


# -- AuthenticationError -------------------------------------------------------


def test_authentication_error_default_code():
    err = AuthenticationError("invalid key")
    assert err.code == "UNAUTHORIZED"
    assert err.status_code == 401


def test_authentication_error_custom_code():
    err = AuthenticationError("forbidden", code="FORBIDDEN")
    assert err.code == "FORBIDDEN"


def test_authentication_error_inherits_from_zyndpay_error():
    err = AuthenticationError("no auth")
    assert isinstance(err, ZyndPayError)


# -- NotFoundError -------------------------------------------------------------


def test_not_found_error_code_and_status():
    err = NotFoundError("resource missing")
    assert err.code == "NOT_FOUND"
    assert err.status_code == 404


def test_not_found_error_inherits_from_zyndpay_error():
    err = NotFoundError("gone")
    assert isinstance(err, ZyndPayError)


# -- RateLimitError ------------------------------------------------------------


def test_rate_limit_error_code_and_status():
    err = RateLimitError("slow down")
    assert err.code == "RATE_LIMIT_EXCEEDED"
    assert err.status_code == 429


def test_rate_limit_error_stores_retry_after():
    err = RateLimitError("slow down", retry_after=30)
    assert err.retry_after == 30


def test_rate_limit_error_inherits_from_zyndpay_error():
    err = RateLimitError("limit")
    assert isinstance(err, ZyndPayError)


# -- All errors inherit from ZyndPayError --------------------------------------


@pytest.mark.parametrize(
    "cls",
    [AuthenticationError, ValidationError, NotFoundError, RateLimitError, ConflictError],
)
def test_all_error_classes_inherit_from_zyndpay_error(cls):
    err = cls("test")
    assert isinstance(err, ZyndPayError)
    assert isinstance(err, Exception)
