"""Unit tests for retries module."""

import pytest


def test_exception_hierarchy():
    from libs.common.retries import (
        ACEFError,
        DependencyError,
        NonRetryableError,
        RetryableError,
        ValidationError,
    )

    assert issubclass(RetryableError, ACEFError)
    assert issubclass(NonRetryableError, ACEFError)
    assert issubclass(ValidationError, ACEFError)
    assert issubclass(DependencyError, ACEFError)


def test_error_fields():
    from libs.common.retries import RetryableError

    err = RetryableError("msg", source="sec", entity="CIK123", context={"url": "x"})
    assert err.source == "sec"
    assert err.entity == "CIK123"
    assert err.context["url"] == "x"


@pytest.mark.asyncio
async def test_with_retry_succeeds_on_first_attempt():
    from libs.common.retries import with_retry

    call_count = 0

    @with_retry(max_attempts=3)
    async def func():
        nonlocal call_count
        call_count += 1
        return "ok"

    result = await func()
    assert result == "ok"
    assert call_count == 1


@pytest.mark.asyncio
async def test_with_retry_retries_on_retryable_error():
    from libs.common.retries import RetryableError, with_retry

    call_count = 0

    @with_retry(max_attempts=3, min_wait=0.01, max_wait=0.1)
    async def func():
        nonlocal call_count
        call_count += 1
        if call_count < 3:
            raise RetryableError("transient")
        return "ok"

    result = await func()
    assert result == "ok"
    assert call_count == 3


@pytest.mark.asyncio
async def test_with_retry_does_not_retry_non_retryable():
    from libs.common.retries import NonRetryableError, with_retry

    call_count = 0

    @with_retry(max_attempts=3, min_wait=0.01, max_wait=0.1)
    async def func():
        nonlocal call_count
        call_count += 1
        raise NonRetryableError("permanent")

    with pytest.raises(NonRetryableError):
        await func()
    assert call_count == 1


@pytest.mark.asyncio
async def test_with_retry_exhaustion():
    """max_attempts 모두 소진 후 RetryableError가 최종 raise된다."""
    from libs.common.retries import RetryableError, with_retry

    call_count = 0

    @with_retry(max_attempts=3, min_wait=0.01, max_wait=0.1)
    async def func():
        nonlocal call_count
        call_count += 1
        raise RetryableError("always fails")

    with pytest.raises(RetryableError):
        await func()
    assert call_count == 3
