"""Tests for Edge convenience methods: prices, last, returns, correlation."""

import httpx
import pandas as pd


# ── Shared mock data ─────────────────────────────────────────────────────────

MULTI_SYMBOL_RESPONSE = {
    "symbols": "ZC*1,ZS*1",
    "exchange": "CME",
    "start_date": "2025-01-01",
    "end_date": "2025-01-03",
    "data_points": 4,
    "data": [
        {"ts_event": "2025-01-01", "open": 450.0, "high": 455.0, "low": 448.0, "close": 453.0, "volume": 1000, "symbol": "ZC*1"},
        {"ts_event": "2025-01-02", "open": 453.0, "high": 460.0, "low": 451.0, "close": 458.0, "volume": 1200, "symbol": "ZC*1"},
        {"ts_event": "2025-01-01", "open": 1000.0, "high": 1010.0, "low": 995.0, "close": 1005.0, "volume": 800, "symbol": "ZS*1"},
        {"ts_event": "2025-01-02", "open": 1005.0, "high": 1020.0, "low": 1000.0, "close": 1015.0, "volume": 900, "symbol": "ZS*1"},
    ],
}

QUOTE_RESPONSE = {
    "symbols": ["ZC*1", "ZS*1"],
    "exchange": "CME",
    "start_ts": "2025-01-01T00:00:00Z",
    "end_ts": "2025-01-02T00:00:00Z",
    "data_points": 2,
    "data": [
        {"symbol": "ZC*1", "last_price": 458.0, "session_high": 460.0, "session_low": 450.0, "volume": 5000},
        {"symbol": "ZS*1", "last_price": 1015.0, "session_high": 1020.0, "session_low": 1000.0, "volume": 3000},
    ],
}


# ── Tests ────────────────────────────────────────────────────────────────────


def test_bare_root_expansion(mock_api, client):
    """edge.history("ZC") should expand to ZC*1 and infer CBOT exchange."""
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json={
            "symbols": "ZC*1", "exchange": "CBOT",
            "start_date": "2025-01-01", "end_date": "2025-01-03",
            "data_points": 1,
            "data": [{"ts_event": "2025-01-01", "open": 450.0, "high": 455.0, "low": 448.0, "close": 453.0, "volume": 1000, "symbol": "ZC*1"}],
        })
    )
    df = client.history("ZC", start="2025-01-01")
    assert len(df) == 1

    # Verify the request was made with expanded symbol (URL-encoded * = %2A)
    req = mock_api.calls.last.request
    assert "ZC%2A1" in str(req.url) or "ZC*1" in str(req.url)


def test_prices_returns_pivot(mock_api, client):
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json=MULTI_SYMBOL_RESPONSE)
    )

    matrix = client.prices(["ZC", "ZS"], start="2025-01-01")
    assert isinstance(matrix, pd.DataFrame)
    assert "ZC*1" in matrix.columns
    assert "ZS*1" in matrix.columns
    assert len(matrix) == 2  # 2 dates


def test_last_scalar(mock_api, client):
    mock_api.get("/futures/quotes/latest").mock(
        return_value=httpx.Response(200, json={
            "symbols": ["ZC*1"], "exchange": "CME",
            "start_ts": "2025-01-01T00:00:00Z", "end_ts": "2025-01-02T00:00:00Z",
            "data_points": 1,
            "data": [{"symbol": "ZC*1", "last_price": 458.0, "volume": 5000}],
        })
    )

    price = client.last("ZC")
    assert isinstance(price, float)
    assert price == 458.0


def test_last_series(mock_api, client):
    mock_api.get("/futures/quotes/latest").mock(
        return_value=httpx.Response(200, json=QUOTE_RESPONSE)
    )

    prices = client.last(["ZC", "ZS"])
    assert isinstance(prices, pd.Series)
    assert len(prices) == 2
    assert prices["ZC*1"] == 458.0
    assert prices["ZS*1"] == 1015.0


def test_last_scalar_fallback_to_history(mock_api, client):
    """When quotes return 503, last() falls back to historical bars."""
    mock_api.get("/futures/quotes/latest").mock(
        return_value=httpx.Response(503, json={"detail": "Realtime market data service not initialised"})
    )
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json={
            "symbols": "ZC*1", "exchange": "CBOT",
            "start_date": "2025-01-01", "end_date": "2025-01-03",
            "data_points": 2,
            "data": [
                {"ts_event": "2025-01-01", "open": 450.0, "high": 455.0, "low": 448.0, "close": 453.0, "volume": 1000, "symbol": "ZC*1"},
                {"ts_event": "2025-01-02", "open": 453.0, "high": 460.0, "low": 451.0, "close": 458.0, "volume": 1200, "symbol": "ZC*1"},
            ],
        })
    )

    price = client.last("ZC")
    assert isinstance(price, float)
    assert price == 458.0  # last historical close


def test_last_series_fallback_to_history(mock_api, client):
    """When quotes return 503, last() falls back for multiple symbols too."""
    mock_api.get("/futures/quotes/latest").mock(
        return_value=httpx.Response(503, json={"detail": "Realtime market data service not initialised"})
    )
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json=MULTI_SYMBOL_RESPONSE)
    )

    prices = client.last(["ZC", "ZS"])
    assert isinstance(prices, pd.Series)
    assert len(prices) == 2
    assert prices["ZC*1"] == 458.0
    assert prices["ZS*1"] == 1015.0


def test_last_fallback_empty(mock_api, client):
    """When both quotes and history return nothing, last() returns NaN."""
    mock_api.get("/futures/quotes/latest").mock(
        return_value=httpx.Response(503, json={"detail": "unavailable"})
    )
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json={
            "symbols": "FAKE*1", "exchange": "CME",
            "start_date": "2025-01-01", "end_date": "2025-01-02",
            "data_points": 0, "data": [],
        })
    )

    import math
    price = client.last("FAKE")
    assert math.isnan(price)


def test_returns_matrix(mock_api, client):
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json=MULTI_SYMBOL_RESPONSE)
    )

    ret = client.returns(["ZC", "ZS"], start="2025-01-01")
    assert isinstance(ret, pd.DataFrame)
    assert "ZC*1" in ret.columns
    assert "ZS*1" in ret.columns
    # 2 dates → 1 return row after pct_change().dropna()
    assert len(ret) == 1
    # ZC went 453→458, ~1.1% return
    assert abs(ret["ZC*1"].iloc[0] - (458.0 / 453.0 - 1)) < 0.001


def test_correlation_matrix(mock_api, client):
    # Need 3+ data points for meaningful correlation
    resp = {
        "symbols": "ZC*1,ZS*1", "exchange": "CME",
        "start_date": "2025-01-01", "end_date": "2025-01-04",
        "data_points": 6,
        "data": [
            {"ts_event": "2025-01-01", "open": 450.0, "high": 455.0, "low": 448.0, "close": 450.0, "volume": 1000, "symbol": "ZC*1"},
            {"ts_event": "2025-01-02", "open": 453.0, "high": 460.0, "low": 451.0, "close": 458.0, "volume": 1200, "symbol": "ZC*1"},
            {"ts_event": "2025-01-03", "open": 458.0, "high": 465.0, "low": 455.0, "close": 462.0, "volume": 1100, "symbol": "ZC*1"},
            {"ts_event": "2025-01-01", "open": 1000.0, "high": 1010.0, "low": 995.0, "close": 1000.0, "volume": 800, "symbol": "ZS*1"},
            {"ts_event": "2025-01-02", "open": 1005.0, "high": 1020.0, "low": 1000.0, "close": 1015.0, "volume": 900, "symbol": "ZS*1"},
            {"ts_event": "2025-01-03", "open": 1015.0, "high": 1025.0, "low": 1010.0, "close": 1022.0, "volume": 950, "symbol": "ZS*1"},
        ],
    }
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json=resp)
    )

    corr = client.correlation(["ZC", "ZS"], start="2025-01-01")
    assert isinstance(corr, pd.DataFrame)
    assert "ZC*1" in corr.columns
    assert "ZS*1" in corr.columns
    # Diagonal should be 1.0
    assert corr.loc["ZC*1", "ZC*1"] == 1.0


def test_relative_date_1y(mock_api, client):
    """edge.history("ZC", start="1Y") should resolve relative date."""
    mock_api.get("/futures/bars/historical").mock(
        return_value=httpx.Response(200, json={
            "symbols": "ZC*1", "exchange": "CBOT",
            "start_date": "2024-03-25", "end_date": "2025-03-25",
            "data_points": 0, "data": [],
        })
    )

    df = client.history("ZC", start="1Y")
    req = mock_api.calls.last.request
    url = str(req.url)
    # Should have resolved to an absolute date, not "1Y"
    assert "1Y" not in url
    assert "start_date=" in url
