"""Tests: Oracle client retries on transport errors and malformed JSON."""
from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest

from libs.oracle_client.client import OracleClient
from libs.oracle_client.exceptions import OracleTransportError


class MockResponse:
    def __init__(self, status_code: int = 200, json_data: dict | None = None, raise_json: bool = False):
        self.status_code = status_code
        self._json_data = json_data or {}
        self._raise_json = raise_json

    def json(self):
        if self._raise_json:
            raise ValueError("Invalid JSON")
        return self._json_data


@pytest.mark.asyncio
async def test_remote_protocol_error_is_retried():
    """httpx.RemoteProtocolError (unwrapped before fix) must be retried."""
    call_count = 0

    async def mock_get(path, params=None):
        nonlocal call_count
        call_count += 1
        raise httpx.RemoteProtocolError("connection reset", request=MagicMock())

    client = OracleClient(base_url="http://oracle:8000")
    mock_http_client = AsyncMock()
    mock_http_client.get = mock_get
    client._client = mock_http_client

    with pytest.raises(OracleTransportError):
        await client.get("/api/v1/events")

    assert call_count == 3  # 3 attempts


@pytest.mark.asyncio
async def test_malformed_json_is_retried():
    """response.json() raising ValueError must raise OracleTransportError."""
    call_count = 0

    async def mock_get(path, params=None):
        nonlocal call_count
        call_count += 1
        return MockResponse(status_code=200, raise_json=True)

    client = OracleClient(base_url="http://oracle:8000")
    mock_http_client = AsyncMock()
    mock_http_client.get = mock_get
    client._client = mock_http_client

    with pytest.raises(OracleTransportError):
        await client.get("/api/v1/events")

    assert call_count == 3


@pytest.mark.asyncio
async def test_read_error_is_retried():
    """httpx.ReadError (subclass of TransportError) must be retried."""
    call_count = 0

    async def mock_get(path, params=None):
        nonlocal call_count
        call_count += 1
        raise httpx.ReadError("connection reset", request=MagicMock())

    client = OracleClient(base_url="http://oracle:8000")
    mock_http_client = AsyncMock()
    mock_http_client.get = mock_get
    client._client = mock_http_client

    with pytest.raises(OracleTransportError):
        await client.get("/api/v1/events")

    assert call_count == 3


@pytest.mark.asyncio
async def test_successful_response_not_retried():
    """Normal 200 response must NOT trigger retry."""
    call_count = 0

    async def mock_get(path, params=None):
        nonlocal call_count
        call_count += 1
        return MockResponse(status_code=200, json_data={"status": "ok"})

    client = OracleClient(base_url="http://oracle:8000")
    mock_http_client = AsyncMock()
    mock_http_client.get = mock_get
    client._client = mock_http_client

    result = await client.get("/api/v1/health")
    assert result == {"status": "ok"}
    assert call_count == 1
