"""The forward read-model DTOs and the state_reader that normalizes heterogeneous tracker state files."""
from __future__ import annotations

import json
import math

from fxhnt.adapters.persistence.state_reader import ForwardStateReader
from fxhnt.application.forward_models import ForwardNavRow, ForwardSummary


def test_forward_summary_total_return_pct() -> None:
    s = ForwardSummary(strategy_id="x", as_of="2026-06-12", days=7, nav=1.05,
                       total_return=0.05, sharpe=1.2, maxdd=-0.04)
    assert round(s.total_return_pct, 2) == 5.0


def test_forward_nav_row_is_immutable() -> None:
    r = ForwardNavRow(strategy_id="x", date="2026-06-05", ret=0.01, nav=1.01)
    assert r.strategy_id == "x" and r.nav == 1.01


def _write(tmp_path, name, obj):
    p = tmp_path / f"{name}.json"
    p.write_text(json.dumps(obj))
    return p


def test_reads_days_list_family(tmp_path) -> None:
    _write(tmp_path, "fxhnt_combined_forward", {
        "inception": "2026-06-04", "last_date": "2026-06-06", "nav": 1.0201,
        "days": [
            {"date": "2026-06-05", "ret": 0.01, "nav": 1.01},
            {"date": "2026-06-06", "ret": 0.0099, "nav": 1.0201},
        ],
    })
    reader = ForwardStateReader()
    summary, rows = reader.read(tmp_path / "fxhnt_combined_forward.json", "combined")
    assert summary.strategy_id == "combined"
    assert summary.days == 2
    assert summary.as_of == "2026-06-06"
    assert math.isclose(summary.nav, 1.0201, rel_tol=1e-9)
    assert [r.date for r in rows] == ["2026-06-05", "2026-06-06"]
    assert all(r.strategy_id == "combined" for r in rows)


def test_reads_welford_equity_family(tmp_path) -> None:
    _write(tmp_path, "multistrat_state", {
        "last_date": "2026-06-12", "days": 5, "sum_r": -0.0007245543702343088,
        "sumsq_r": 1.8807003187214318e-05, "equity": 0.9992663073361069,
        "peak": 1.001482883251981, "max_dd": -0.004599760548340659,
    })
    reader = ForwardStateReader()
    summary, rows = reader.read(tmp_path / "multistrat_state.json", "multistrat")
    assert summary.days == 5
    assert summary.as_of == "2026-06-12"
    assert math.isclose(summary.nav, 0.9992663073361069, rel_tol=1e-9)
    assert math.isclose(summary.total_return, -0.0007336926638931, rel_tol=1e-6)
    assert math.isclose(summary.maxdd, -0.004599760548340659, rel_tol=1e-9)
    # snapshot-only: one forward point at as_of with nav == equity
    assert len(rows) == 1 and rows[0].date == "2026-06-12"
    assert math.isclose(rows[0].nav, 0.9992663073361069, rel_tol=1e-9)


def test_reads_cumulative_funding_family(tmp_path) -> None:
    _write(tmp_path, "crossvenue_state", {
        "positions": {"LAB": 0.2, "TON": 0.2}, "cum_gross": 0.0046, "cum_net": -6.11e-05,
        "days": 8, "last_run_date": "2026-06-14",
    })
    reader = ForwardStateReader()
    summary, rows = reader.read(tmp_path / "crossvenue_state.json", "crossvenue")
    assert summary.days == 8
    assert summary.as_of == "2026-06-14"
    assert math.isclose(summary.total_return, -6.11e-05, rel_tol=1e-9)
    assert math.isclose(summary.nav, 1.0 - 6.11e-05, rel_tol=1e-9)
    assert summary.sharpe == 0.0          # not derivable from cumulative-only
    assert len(rows) == 1 and rows[0].date == "2026-06-14"


def test_unknown_shape_raises_valueerror(tmp_path) -> None:
    _write(tmp_path, "weird", {"foo": 1})
    reader = ForwardStateReader()
    try:
        reader.read(tmp_path / "weird.json", "weird")
        assert False, "expected ValueError"
    except ValueError:
        pass


def test_welford_sample_variance_sharpe(tmp_path) -> None:
    """Verify _welford uses SAMPLE variance (/(n-1)) matching the days-list _sharpe path.

    Fixture: days=2, r1=0.01, r2=0.03
      sum_r   = 0.04
      sumsq_r = 0.01**2 + 0.03**2 = 0.001
      mean    = 0.02
      sample var = (0.001 - 0.04**2/2) / (2-1) = (0.001 - 0.0008) / 1 = 0.0002
      std     = sqrt(0.0002)
      sharpe  = (0.02 / sqrt(0.0002)) * sqrt(365)
    """
    sum_r = 0.04
    sumsq_r = 0.01 ** 2 + 0.03 ** 2   # = 0.001
    mean = sum_r / 2
    sample_var = (sumsq_r - sum_r ** 2 / 2) / (2 - 1)
    expected_sharpe = mean / math.sqrt(sample_var) * math.sqrt(365.0)

    _write(tmp_path, "welford_fixture", {
        "last_date": "2026-06-10", "days": 2,
        "sum_r": sum_r, "sumsq_r": sumsq_r,
        "equity": 1.04, "max_dd": 0.0,
    })
    reader = ForwardStateReader()
    summary, _ = reader.read(tmp_path / "welford_fixture.json", "w")
    assert summary.sharpe > 0, "sharpe must be positive for two positive returns"
    assert math.isclose(summary.sharpe, expected_sharpe, rel_tol=1e-9)
