"""Tests for HttpTransport retry behavior."""

from __future__ import annotations

import pathlib
import sys
import unittest
from unittest import mock


ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
    sys.path.insert(0, str(SRC))

from vedatrace.config import RetryConfig
from vedatrace.models import LogLevel, LogRecord
from vedatrace.transports.http import HttpTransport


class _FakeResponse:
    def __init__(self, status: int = 200) -> None:
        self.status = status

    def __enter__(self) -> "_FakeResponse":
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        return None

    def getcode(self) -> int:
        return self.status


class TestHttpTransportRetryBehavior(unittest.TestCase):
    def _make_record(self) -> LogRecord:
        return LogRecord.create(
            level=LogLevel.INFO,
            message="x",
            service="svc",
            timestamp="2026-02-19T17:42:47.957694Z",
            metadata={},
        )

    def test_retries_until_success_without_on_error(self) -> None:
        outcomes: list[object] = [
            RuntimeError("first failure"),
            RuntimeError("second failure"),
            _FakeResponse(status=200),
        ]
        urlopen_calls = 0
        sleep_calls: list[float] = []
        on_error_calls: list[Exception] = []

        def fake_urlopen(req, timeout):  # type: ignore[no-untyped-def]
            nonlocal urlopen_calls
            _ = req
            _ = timeout
            urlopen_calls += 1
            outcome = outcomes.pop(0)
            if isinstance(outcome, Exception):
                raise outcome
            return outcome

        def sleep_spy(seconds: float) -> None:
            sleep_calls.append(seconds)

        transport = HttpTransport(
            api_key="k",
            retry=RetryConfig(max_retries=2, retry_delay_seconds=0.25),
            on_error=on_error_calls.append,
            sleep_fn=sleep_spy,
        )

        with mock.patch("vedatrace.transports.http.urlrequest.urlopen", new=fake_urlopen):
            transport.emit([self._make_record()])

        self.assertEqual(urlopen_calls, 3)
        self.assertEqual(sleep_calls, [0.25, 0.25])
        self.assertEqual(on_error_calls, [])

    def test_retries_then_reports_error_once_when_exhausted(self) -> None:
        outcomes: list[object] = [
            RuntimeError("first failure"),
            RuntimeError("second failure"),
        ]
        urlopen_calls = 0
        sleep_calls: list[float] = []
        on_error_calls: list[Exception] = []

        def fake_urlopen(req, timeout):  # type: ignore[no-untyped-def]
            nonlocal urlopen_calls
            _ = req
            _ = timeout
            urlopen_calls += 1
            outcome = outcomes.pop(0)
            if isinstance(outcome, Exception):
                raise outcome
            return outcome

        def sleep_spy(seconds: float) -> None:
            sleep_calls.append(seconds)

        transport = HttpTransport(
            api_key="k",
            retry=RetryConfig(max_retries=1, retry_delay_seconds=0.1),
            on_error=on_error_calls.append,
            sleep_fn=sleep_spy,
        )

        with mock.patch("vedatrace.transports.http.urlrequest.urlopen", new=fake_urlopen):
            transport.emit([self._make_record()])

        self.assertEqual(urlopen_calls, 2)
        self.assertEqual(sleep_calls, [0.1])
        self.assertEqual(len(on_error_calls), 1)
        self.assertIsInstance(on_error_calls[0], RuntimeError)
        self.assertEqual(str(on_error_calls[0]), "second failure")


if __name__ == "__main__":
    unittest.main()
