"""Tests for TherminalClient."""

import pytest
from pytest_httpx import HTTPXMock

from therminal import NotFoundError, RateLimitError, ServerError, TherminalClient, ValidationError

# ── Health ───────────────────────────────────────────────────

def test_health(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json={"status": "ok", "duckdb": "ok", "supabase": "ok"})
    result = client.health()
    assert result["status"] == "ok"
    assert result["duckdb"] == "ok"


# ── Series ───────────────────────────────────────────────────

def test_series_list(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ticker": "KXHIGHNY", "measure_type": "high", "station_code": "NYC", "active": True},
    ])
    result = client.series()
    assert len(result) == 1
    assert result[0]["ticker"] == "KXHIGHNY"


def test_series_detail(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json={"ticker": "KXHIGHNY", "station_code": "NYC"})
    result = client.series_detail("KXHIGHNY")
    assert result["ticker"] == "KXHIGHNY"


def test_series_not_found(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(status_code=404, json={"error": "series not found"})
    with pytest.raises(NotFoundError):
        client.series_detail("NONEXISTENT")


# ── Markets ──────────────────────────────────────────────────

def test_markets_list(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ticker": "KXHIGHNY-26MAR20-T50", "series_ticker": "KXHIGHNY", "status": "active"},
    ])
    result = client.markets(series="KXHIGHNY")
    assert len(result) == 1


def test_market_detail(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json={"ticker": "KXHIGHNY-26MAR20-T50", "status": "active"})
    result = client.market("KXHIGHNY-26MAR20-T50")
    assert result["status"] == "active"


# ── Candles ──────────────────────────────────────────────────

def test_candles(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"market_ticker": "T1", "period_interval": 5, "end_period_ts": "2026-03-20T10:00:00Z",
         "open": 42, "high": 45, "low": 40, "close": 43, "volume": 100, "open_interest": 500,
         "filled": False},
    ])
    result = client.candles(market="T1", from_date="2026-03-20", to_date="2026-03-20")
    assert len(result) == 1
    assert result[0]["open"] == 42


def test_candles_date_normalization(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[])
    client.candles(from_date="2026-03-01", to_date="2026-03-20")
    request = httpx_mock.get_request()
    assert "from=2026-03-01T00%3A00%3A00Z" in str(request.url)


def test_candles_fill_false(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[])
    client.candles(fill=False)
    request = httpx_mock.get_request()
    assert "fill=false" in str(request.url)


# ── Observations ─────────────────────────────────────────────

def test_observations(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"station_code": "NYC", "observed_at": "2026-03-20T12:00:00Z",
         "observation_type": "METAR", "temp_f": 51.1},
    ])
    result = client.observations(station="NYC")
    assert result[0]["temp_f"] == 51.1


def test_observations_metric(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[])
    client.observations(station="NYC", units="metric")
    request = httpx_mock.get_request()
    assert "units=metric" in str(request.url)


# ── Climate ──────────────────────────────────────────────────

def test_climate(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"station_code": "NYC", "observation_date": "2026-03-19",
         "high_temp_f": 44, "low_temp_f": 32, "report_type": "final"},
    ])
    result = client.climate(station="NYC", from_date="2026-03-19")
    assert result[0]["high_temp_f"] == 44


# ── Climate Gaps ─────────────────────────────────────────────

def test_climate_gaps(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {
            "station_code": "NYC", "gap_start": "2026-03-20",
            "gap_end": "2026-03-20", "missing_days": 1,
        },
    ])
    result = client.climate_gaps(station="NYC")
    assert result[0]["missing_days"] == 1


# ── Format Downloads ─────────────────────────────────────────

def test_csv_format(httpx_mock: HTTPXMock, client: TherminalClient, tmp_path):
    httpx_mock.add_response(
        content=b"station_code,high_temp_f\nNYC,44\n",
        headers={"content-type": "text/csv"},
    )
    out = tmp_path / "test.csv"
    path = client.climate(station="NYC", format="csv", save_path=out)
    assert path.exists()
    assert b"NYC" in path.read_bytes()


def test_parquet_format(httpx_mock: HTTPXMock, client: TherminalClient, tmp_path):
    httpx_mock.add_response(
        content=b"PAR1_FAKE_BYTES",
        headers={"content-type": "application/vnd.apache.parquet"},
    )
    out = tmp_path / "test.parquet"
    path = client.candles(market="T1", format="parquet", save_path=out)
    assert path.exists()


def test_columns_param(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[{"temp_f": 51.1}])
    client.observations(
        station="NYC", columns=["temp_f", "wind_speed_kt"],
    )
    request = httpx_mock.get_request()
    assert "columns=temp_f%2Cwind_speed_kt" in str(request.url)


# ── Error Handling ───────────────────────────────────────────

def test_rate_limit(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(
        status_code=429,
        headers={"Retry-After": "5"},
        json={"error": "rate limit exceeded"},
    )
    with pytest.raises(RateLimitError) as exc_info:
        client.health()
    assert exc_info.value.retry_after == 5


def test_validation_error(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(status_code=400, json={"error": "interval must be an integer"})
    with pytest.raises(ValidationError):
        client.candles(interval=999)


def test_server_error(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(status_code=500, json={"error": "internal server error"})
    with pytest.raises(ServerError):
        client.health()


# ── DataFrame ────────────────────────────────────────────────

def test_as_dataframe(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"market_ticker": "T1", "end_period_ts": "2026-03-20T10:00:00Z", "open": 42,
         "high": 45, "low": 40, "close": 43, "volume": 100, "open_interest": 500, "filled": False},
        {"market_ticker": "T1", "end_period_ts": "2026-03-20T10:05:00Z", "open": 43,
         "high": 46, "low": 41, "close": 44, "volume": 50, "open_interest": 510, "filled": False},
    ])
    df = client.candles(as_dataframe=True)
    assert df.index.name == "end_period_ts"
    assert len(df) == 2
    assert df.iloc[0]["open"] == 42


# ── LOB ─────────────────────────────────────────────────────

def test_lob_single_market(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ts": 1000000000, "market_id": "KXTEST-B45", "best_bid": 45, "best_ask": 55,
         "spread": 10, "mid": 50.0, "bids": [{"price": 45, "qty": 100}],
         "asks": [{"price": 55, "qty": 200}]},
    ])
    result = client.lob(market="KXTEST-B45", date="2026-03-01")
    assert len(result) == 1
    assert result[0]["market_id"] == "KXTEST-B45"
    assert result[0]["best_bid"] == 45


def test_lob_series(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ts": 1000000000, "market_id": "KXTEST-B45", "best_bid": 45, "best_ask": 55,
         "spread": 10, "mid": 50.0, "bids": [], "asks": []},
        {"ts": 1000000000, "market_id": "KXTEST-B50", "best_bid": 50, "best_ask": 60,
         "spread": 10, "mid": 55.0, "bids": [], "asks": []},
    ])
    result = client.lob(series="KXTEST", date="2026-03-01")
    assert len(result) == 2


def test_lob_polymarket(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ts": 1000000000, "market_id": "0xabc:0xdef", "best_bid": 30, "best_ask": 70,
         "spread": 40, "mid": 50.0, "bids": [], "asks": []},
    ])
    result = client.lob(market="0xabc:0xdef", source="polymarket", date="2026-03-01")
    assert result[0]["market_id"] == "0xabc:0xdef"


def test_lob_custom_params(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[])
    client.lob(market="X", date="2026-03-01", interval=300, levels=25, hour=14)
    request = httpx_mock.get_requests()[0]
    assert "interval=300" in str(request.url)
    assert "levels=25" in str(request.url)
    assert "hour=14" in str(request.url)


def test_lob_csv_format(httpx_mock: HTTPXMock, client: TherminalClient, tmp_path):
    httpx_mock.add_response(
        content=b"ts,market_id,best_bid\n1000,KXTEST,45\n",
        headers={"content-type": "text/csv"},
    )
    out = tmp_path / "lob.csv"
    path = client.lob(market="X", date="2026-03-01", format="csv", save_path=out)
    assert path.exists()


def test_lob_dataframe(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ts": 1000000000, "market_id": "X", "best_bid": 45, "best_ask": 55,
         "spread": 10, "mid": 50.0, "bids": [], "asks": []},
        {"ts": 2000000000, "market_id": "X", "best_bid": 46, "best_ask": 54,
         "spread": 8, "mid": 50.0, "bids": [], "asks": []},
    ])
    df = client.lob(market="X", date="2026-03-01", as_dataframe=True)
    assert len(df) == 2
    assert "best_bid" in df.columns


def test_lob_not_found(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(status_code=404, json={"error": "no LOB data found"})
    with pytest.raises(NotFoundError):
        client.lob(market="NONEXISTENT", date="2026-03-01")


def test_lob_date_range(httpx_mock: HTTPXMock, client: TherminalClient):
    httpx_mock.add_response(json=[
        {"ts": 1000000000, "market_id": "X", "best_bid": 45, "best_ask": 55,
         "spread": 10, "mid": 50.0, "bids": [], "asks": []},
    ])
    result = client.lob(market="X", from_date="2026-03-01", to_date="2026-03-03", format="ndjson")
    assert len(result) == 1


# ── Context Manager ──────────────────────────────────────────

def test_context_manager(httpx_mock: HTTPXMock):
    httpx_mock.add_response(json={"status": "ok"})
    with TherminalClient(base_url="https://test.api") as client:
        result = client.health()
    assert result["status"] == "ok"


# ── Helpers ──────────────────────────────────────────────────

def test_normalize_date():
    from therminal._http import normalize_date
    assert normalize_date(None) is None
    assert normalize_date("2026-03-01") == "2026-03-01T00:00:00Z"
    assert normalize_date("2026-03-01T12:00:00Z") == "2026-03-01T12:00:00Z"
