import os
import pytest
import respx
import httpx
import rftools
from rftools.client import Client, _DEFAULT_BASE_URL
from rftools.exceptions import AuthError, NotFoundError, RateLimitError, ValidationError, APIError

MOCK_CALC_RESPONSE = {
    "slug": "vswr-return-loss",
    "values": {"returnLoss": 9.54, "reflectionCoeff": 0.333},
    "warnings": [],
    "errors": [],
}

MOCK_BATCH_RESPONSE = {
    "results": [
        {"slug": "vswr-return-loss", "values": {"returnLoss": 13.98}, "warnings": []},
        {"slug": "vswr-return-loss", "values": {"returnLoss": 9.54}, "warnings": []},
    ]
}


@respx.mock
def test_calculate_success():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(200, json=MOCK_CALC_RESPONSE)
    )
    client = Client(api_key="rfc_test")
    result = client.calculate("vswr-return-loss", {"vswr": 2.5})
    assert result["returnLoss"] == 9.54
    assert result.slug == "vswr-return-loss"


@respx.mock
def test_calculate_auth_error():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(401, json={"detail": "Invalid API key"})
    )
    client = Client(api_key="rfc_bad")
    with pytest.raises(AuthError):
        client.calculate("vswr-return-loss", {"vswr": 2.5})


@respx.mock
def test_calculate_quota_exceeded():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(401, json={"detail": "Invalid or quota-exceeded API key"})
    )
    client = Client(api_key="rfc_test")
    with pytest.raises(RateLimitError):
        client.calculate("vswr-return-loss", {"vswr": 2.5})


@respx.mock
def test_calculate_not_found():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(404, json={"detail": "Calculator 'bad-slug' not found"})
    )
    client = Client(api_key="rfc_test")
    with pytest.raises(NotFoundError):
        client.calculate("bad-slug", {})


@respx.mock
def test_batch_success():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate/batch").mock(
        return_value=httpx.Response(200, json=MOCK_BATCH_RESPONSE)
    )
    client = Client(api_key="rfc_test")
    results = client.batch([
        ("vswr-return-loss", {"vswr": 1.5}),
        ("vswr-return-loss", {"vswr": 2.0}),
    ])
    assert len(results) == 2
    assert all(r.ok for r in results)
    assert results[0].values["returnLoss"] == 13.98


def test_batch_requires_api_key():
    client = Client(base_url="http://localhost:9999")  # no key, won't hit network
    with pytest.raises(AuthError, match="API key"):
        client.batch([("vswr-return-loss", {"vswr": 1.5})])


def test_batch_too_many_items():
    client = Client(api_key="rfc_test")
    items = [("vswr-return-loss", {"vswr": 1.0})] * 51
    with pytest.raises(ValidationError, match="50"):
        client.batch(items)


@respx.mock
def test_server_error_retries():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(503, json={"detail": "Service unavailable"})
    )
    client = Client(api_key="rfc_test", max_retries=1)
    with pytest.raises(APIError, match="503"):
        client.calculate("vswr-return-loss", {"vswr": 2.5})


@respx.mock
def test_env_var_api_key(monkeypatch):
    monkeypatch.setenv("RFTOOLS_API_KEY", "rfc_from_env")
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(200, json=MOCK_CALC_RESPONSE)
    )
    client = Client()  # no explicit key
    assert client._api_key == "rfc_from_env"
    result = client.calculate("vswr-return-loss", {"vswr": 2.5})
    assert result["returnLoss"] == 9.54


@respx.mock
async def test_async_calculate_success():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate").mock(
        return_value=httpx.Response(200, json=MOCK_CALC_RESPONSE)
    )
    async with rftools.AsyncClient(api_key="rfc_test") as client:
        result = await client.calculate("vswr-return-loss", {"vswr": 2.5})
    assert result["returnLoss"] == 9.54


@respx.mock
async def test_async_batch_success():
    respx.post(f"{_DEFAULT_BASE_URL}/calculate/batch").mock(
        return_value=httpx.Response(200, json=MOCK_BATCH_RESPONSE)
    )
    async with rftools.AsyncClient(api_key="rfc_test") as client:
        results = await client.batch([
            ("vswr-return-loss", {"vswr": 1.5}),
            ("vswr-return-loss", {"vswr": 2.0}),
        ])
    assert len(results) == 2
    assert results[0].ok
