"""TiingoDailyClient — native total-return adjClose (equity/ETF) + crypto routing. NO live network:
the `_get` HTTP layer is monkeypatched to return fixed fixtures.

Invariants proven here:
  (1) equity symbols hit the daily endpoint and read `adjClose` (NOT raw `close`).
  (2) crypto `BTC-USD` is routed to the crypto endpoint, ticker mapped to `btcusd`, and the nested
      `priceData[].close` is read (crypto has no adjClose).
  (3) the request window's startDate is computed from `lookback_days` (today - lookback_days).
"""
from __future__ import annotations

import datetime as dt
from typing import Any

from fxhnt.adapters.data.tiingo_daily import TiingoDailyClient


def _daily(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Tiingo equity/ETF daily response: a flat list with both `close` and `adjClose`."""
    return [
        {
            "date": f"{r['date']}T00:00:00.000Z",
            "close": r["close"],
            "adjClose": r["adjClose"],
            "divCash": 0.0,
            "splitFactor": 1.0,
        }
        for r in rows
    ]


def _crypto(ticker: str, rows: list[tuple[str, float]]) -> list[dict[str, Any]]:
    """Tiingo crypto response: a one-element list with nested `priceData` (close only, no adjClose)."""
    return [
        {
            "ticker": ticker,
            "baseCurrency": "btc",
            "quoteCurrency": "usd",
            "priceData": [
                {"date": f"{d}T00:00:00.000Z", "open": c, "high": c, "low": c, "close": c, "volume": 1.0}
                for d, c in rows
            ],
        }
    ]


def test_equity_uses_adjclose_not_close() -> None:
    client = TiingoDailyClient(api_key="x")
    # adjClose deliberately differs from close so we can prove which field is read.
    rows = [
        {"date": "2026-01-02", "close": 100.0, "adjClose": 90.0},
        {"date": "2026-01-03", "close": 101.0, "adjClose": 91.5},
        {"date": "2026-01-06", "close": 102.0, "adjClose": 93.0},
    ]
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        assert "/tiingo/daily/SPY/prices" in url
        return _daily(rows)

    client._get = fake_get
    out = client.adj_closes("SPY")

    assert out == {"2026-01-02": 90.0, "2026-01-03": 91.5, "2026-01-06": 93.0}
    assert all(isinstance(k, str) and isinstance(v, float) for k, v in out.items())
    # Daily endpoint, NOT crypto.
    assert "/tiingo/crypto/" not in captured["url"]


def test_crypto_btc_routed_and_ticker_mapped() -> None:
    client = TiingoDailyClient(api_key="x")
    rows = [("2026-01-02", 45000.0), ("2026-01-03", 46000.5), ("2026-01-06", 44000.25)]
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        # Routed to the crypto endpoint with the Yahoo ticker mapped to btcusd.
        assert "/tiingo/crypto/prices" in url
        assert "tickers=btcusd" in url
        assert "resampleFreq=1day" in url
        assert "/tiingo/daily/" not in url
        return _crypto("btcusd", rows)

    client._get = fake_get
    out = client.adj_closes("BTC-USD")

    # Nested priceData[].close is read (crypto has no adjClose).
    assert out == {"2026-01-02": 45000.0, "2026-01-03": 46000.5, "2026-01-06": 44000.25}
    assert "tickers=btcusd" in captured["url"]


def test_startdate_window_from_lookback_days() -> None:
    client = TiingoDailyClient(api_key="x", lookback_days=1100)
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        return _daily([{"date": "2026-01-02", "close": 100.0, "adjClose": 100.0}])

    client._get = fake_get
    client.adj_closes("SPY")

    expected_start = (dt.date.today() - dt.timedelta(days=1100)).isoformat()
    expected_end = dt.date.today().isoformat()
    assert f"startDate={expected_start}" in captured["url"]
    assert f"endDate={expected_end}" in captured["url"]


def test_crypto_window_from_lookback_days() -> None:
    client = TiingoDailyClient(api_key="x", lookback_days=365)
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        return _crypto("btcusd", [("2026-01-02", 45000.0)])

    client._get = fake_get
    client.adj_closes("BTC-USD")

    expected_start = (dt.date.today() - dt.timedelta(days=365)).isoformat()
    assert f"startDate={expected_start}" in captured["url"]


def test_empty_equity_response_returns_empty_dict() -> None:
    client = TiingoDailyClient(api_key="x")
    client._get = lambda url, tries=4: []  # noqa: E731
    assert client.adj_closes("SPY") == {}


def test_empty_crypto_response_returns_empty_dict() -> None:
    client = TiingoDailyClient(api_key="x")
    client._get = lambda url, tries=4: []  # noqa: E731
    assert client.adj_closes("BTC-USD") == {}
