import json

import httpx
import pytest
import respx

from horatio_data_provider import DataProviderClient

BASE = "http://test"


@pytest.fixture
def client():
    return DataProviderClient(BASE)


@respx.mock
@pytest.mark.asyncio
async def test_raw_trades_url_and_body(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/raw_trades/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.raw_trades("BTC").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "BTC"
    assert body["since"] == "2025-01-01T00:00:00Z"
    assert body["until"] == "2025-01-02T00:00:00Z"


@respx.mock
@pytest.mark.asyncio
async def test_ohlcv_url_and_window(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/ohlcv/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.ohlcv("BTC", "1h").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "BTC"
    assert body["window"] == "1h"


@respx.mock
@pytest.mark.asyncio
async def test_raw_trades_cache(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/raw_trades/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.raw_trades("BTC").time_range("2025-01-01", "2025-01-02").cache("force_replace").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["cache"] is True
    assert body["cache_type"] == "force_replace"


@respx.mock
@pytest.mark.asyncio
async def test_flush_raw_trades(client):
    route = respx.post(f"{BASE}/binance/raw_trades/flush").mock(
        return_value=httpx.Response(200, content=b"{}", headers={"content-type": "application/json"})
    )
    await client.binance.flush_raw_trades(token="BTC")
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "BTC"


@respx.mock
@pytest.mark.asyncio
async def test_ohlcv_cache_sets_body_fields(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/ohlcv/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.ohlcv("BTC", "1h").time_range("2025-01-01", "2025-01-02").cache("append").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["cache"] is True
    assert body["cache_type"] == "append"


@respx.mock
@pytest.mark.asyncio
async def test_flush_ohlcv(client):
    route = respx.post(f"{BASE}/binance/ohlcv/flush").mock(
        return_value=httpx.Response(200, content=b"{}", headers={"content-type": "application/json"})
    )
    await client.binance.flush_ohlcv(token="BTC", window="1h")
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body == {"token": "BTC", "window": "1h"}


@respx.mock
@pytest.mark.asyncio
async def test_book_depth_url_and_body(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/book_depth/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.book_depth("BTC").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "BTC"
    assert body["since"] == "2025-01-01T00:00:00Z"
    assert body["until"] == "2025-01-02T00:00:00Z"


@respx.mock
@pytest.mark.asyncio
async def test_book_depth_cache(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/book_depth/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.book_depth("BTC").time_range("2025-01-01", "2025-01-02").cache("force_replace").as_pandas()
    body = json.loads(route.calls[0].request.content)
    assert body["cache"] is True
    assert body["cache_type"] == "force_replace"


@respx.mock
@pytest.mark.asyncio
async def test_open_interest_url_and_body(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/open_interest/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.open_interest("ETH").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "ETH"


@respx.mock
@pytest.mark.asyncio
async def test_funding_rate_url_and_body(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/funding_rate/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.funding_rate("ETH").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "ETH"


@respx.mock
@pytest.mark.asyncio
async def test_long_short_ratios_url_and_body(client, parquet_bytes):
    route = respx.post(f"{BASE}/binance/long_short_ratios/read").mock(
        return_value=httpx.Response(200, content=parquet_bytes, headers={"content-type": "application/octet-stream"})
    )
    await client.binance.long_short_ratios("BTC").time_range("2025-01-01", "2025-01-02").as_pandas()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body["token"] == "BTC"


@respx.mock
@pytest.mark.asyncio
async def test_flush_exchange_with_endpoint_and_token(client):
    route = respx.post(f"{BASE}/binance/exchange/flush").mock(
        return_value=httpx.Response(200, content=b"{}", headers={"content-type": "application/json"})
    )
    await client.binance.flush_exchange(endpoint="book_depth", token="BTC")
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body == {"endpoint": "book_depth", "token": "BTC"}


@respx.mock
@pytest.mark.asyncio
async def test_flush_exchange_empty_body(client):
    route = respx.post(f"{BASE}/binance/exchange/flush").mock(
        return_value=httpx.Response(200, content=b"{}", headers={"content-type": "application/json"})
    )
    await client.binance.flush_exchange()
    assert route.called
    body = json.loads(route.calls[0].request.content)
    assert body == {}
