"""TiingoFundamentalsClient — latest daily valuation metrics + latest reported statement metrics.
NO live network: the `_get` HTTP layer is monkeypatched to return fixed fixtures.

Invariants proven here:
  (1) metrics() hits the fundamentals/daily endpoint and returns the LATEST (max date) row's
      valuation fields (peRatio/pbRatio/marketCap, ...) as floats, skipping None.
  (2) statement_metrics() hits the fundamentals/statements endpoint, picks the LATEST (max date)
      entry, and flattens its statementData sections into a {dataCode: float(value)} dict.
  (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_fundamentals import TiingoFundamentalsClient


def _daily(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Tiingo fundamentals/daily response: a flat list of valuation-metric rows."""
    return [
        {
            "date": f"{r['date']}T00:00:00.000Z",
            "marketCap": r.get("marketCap"),
            "enterpriseVal": r.get("enterpriseVal"),
            "peRatio": r.get("peRatio"),
            "pbRatio": r.get("pbRatio"),
            "trailingPEG1Y": r.get("trailingPEG1Y"),
        }
        for r in rows
    ]


def _statements(entries: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Tiingo fundamentals/statements response: list of period entries with nested statementData."""
    return entries


def test_metrics_returns_latest_daily_row() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    # Out-of-order on purpose: the latest (max date) row must be chosen, not the first/last.
    rows = [
        {"date": "2026-01-02", "marketCap": 100.0, "peRatio": 20.0, "pbRatio": 3.0,
         "enterpriseVal": 110.0, "trailingPEG1Y": 1.5},
        {"date": "2026-01-06", "marketCap": 120.0, "peRatio": 22.0, "pbRatio": 3.3,
         "enterpriseVal": 130.0, "trailingPEG1Y": 1.7},
        {"date": "2026-01-03", "marketCap": 110.0, "peRatio": 21.0, "pbRatio": 3.1,
         "enterpriseVal": 120.0, "trailingPEG1Y": 1.6},
    ]
    captured: dict[str, str] = {}

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

    client._get = fake_get
    out = client.metrics("AAPL")

    # Latest row (2026-01-06) chosen.
    assert out["marketCap"] == 120.0
    assert out["peRatio"] == 22.0
    assert out["pbRatio"] == 3.3
    assert out["enterpriseVal"] == 130.0
    assert out["trailingPEG1Y"] == 1.7
    assert "date" not in out
    assert all(isinstance(v, float) for v in out.values())
    assert "/statements" not in captured["url"]


def test_metrics_skips_none_values() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    rows = [
        {"date": "2026-01-06", "marketCap": 120.0, "peRatio": None, "pbRatio": 3.3,
         "enterpriseVal": None, "trailingPEG1Y": 1.7},
    ]
    client._get = lambda url, tries=4: _daily(rows)  # noqa: E731
    out = client.metrics("AAPL")
    assert "peRatio" not in out
    assert "enterpriseVal" not in out
    assert out["marketCap"] == 120.0
    assert out["pbRatio"] == 3.3


def test_statement_metrics_flattens_latest_overview() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    entries = [
        {
            "date": "2025-09-30T00:00:00.000Z", "year": 2025, "quarter": 3,
            "statementData": {
                "overview": [
                    {"dataCode": "roe", "value": 0.10},
                    {"dataCode": "piotroskiFScore", "value": 5},
                ],
            },
        },
        {
            "date": "2025-12-31T00:00:00.000Z", "year": 2025, "quarter": 4,
            "statementData": {
                "overview": [
                    {"dataCode": "roe", "value": 0.42},
                    {"dataCode": "piotroskiFScore", "value": 8},
                    {"dataCode": "debtEquity", "value": 1.25},
                    {"dataCode": "grossMargin", "value": 0.46},
                ],
                "incomeStatement": [
                    {"dataCode": "revenue", "value": 1000.0},
                ],
                "balanceSheet": [
                    {"dataCode": "totalAssets", "value": 5000.0},
                ],
                "cashFlow": [
                    {"dataCode": "freeCashFlow", "value": 250.0},
                ],
            },
        },
    ]
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        assert "/tiingo/fundamentals/AAPL/statements" in url
        return _statements(entries)

    client._get = fake_get
    out = client.statement_metrics("AAPL")

    # Latest entry (2025-12-31) chosen — NOT the 2025-09-30 one.
    assert out["roe"] == 0.42
    assert out["piotroskiFScore"] == 8.0
    assert out["debtEquity"] == 1.25
    assert out["grossMargin"] == 0.46
    # Other sections flattened too.
    assert out["revenue"] == 1000.0
    assert out["totalAssets"] == 5000.0
    assert out["freeCashFlow"] == 250.0
    assert all(isinstance(v, float) for v in out.values())
    assert "/daily" not in captured["url"].split("/statements")[0] + "/statements"


def test_statement_metrics_skips_none_values() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    entries = [
        {
            "date": "2025-12-31T00:00:00.000Z",
            "statementData": {
                "overview": [
                    {"dataCode": "roe", "value": 0.42},
                    {"dataCode": "debtEquity", "value": None},
                ],
            },
        },
    ]
    client._get = lambda url, tries=4: entries  # noqa: E731
    out = client.statement_metrics("AAPL")
    assert out["roe"] == 0.42
    assert "debtEquity" not in out


def test_metrics_startdate_window_from_lookback_days() -> None:
    client = TiingoFundamentalsClient(api_key="x", lookback_days=800)
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        return _daily([{"date": "2026-01-06", "marketCap": 1.0, "peRatio": 1.0, "pbRatio": 1.0}])

    client._get = fake_get
    client.metrics("AAPL")
    expected_start = (dt.date.today() - dt.timedelta(days=800)).isoformat()
    assert f"startDate={expected_start}" in captured["url"]


def test_statement_metrics_startdate_window_from_lookback_days() -> None:
    client = TiingoFundamentalsClient(api_key="x", lookback_days=800)
    captured: dict[str, str] = {}

    def fake_get(url: str, tries: int = 4) -> Any:
        captured["url"] = url
        return _statements([
            {"date": "2025-12-31T00:00:00.000Z",
             "statementData": {"overview": [{"dataCode": "roe", "value": 0.1}]}},
        ])

    client._get = fake_get
    client.statement_metrics("AAPL")
    expected_start = (dt.date.today() - dt.timedelta(days=800)).isoformat()
    assert f"startDate={expected_start}" in captured["url"]


def test_empty_metrics_response_returns_empty_dict() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    client._get = lambda url, tries=4: []  # noqa: E731
    assert client.metrics("AAPL") == {}


def test_empty_statements_response_returns_empty_dict() -> None:
    client = TiingoFundamentalsClient(api_key="x")
    client._get = lambda url, tries=4: []  # noqa: E731
    assert client.statement_metrics("AAPL") == {}
