"""LeveredShadowStrategy — the observe-only levered shadow wrapper (multistrat_levered): levers a base
recompute-replay strategy's daily returns to the fund vol target (capped, floored at 1x) and charges the
honest financing drag on the borrowed leg. Return-defined; a pure transform of the base series."""
from __future__ import annotations

import math
from typing import Any

from fxhnt.application.paper_strategies import (
    LeveredShadowStrategy,
    leverage_financing_sensitivity,
)


class _Base:
    """A stub base strategy returning a fixed (date, ret) series + passing `extra` through unchanged."""
    def __init__(self, rows: list[tuple[str, float]]) -> None:
        self._rows = rows

    def advance(self, last_date: str | None, extra: dict[str, Any]) -> tuple[list[tuple[str, float]], dict[str, Any]]:
        return list(self._rows), extra


def _ann_vol(rets: list[float]) -> float:
    mean = sum(rets) / len(rets)
    return math.sqrt(sum((r - mean) ** 2 for r in rets) / (len(rets) - 1)) * math.sqrt(252)


def test_levers_a_low_vol_book_to_target_minus_financing() -> None:
    # A low-vol base (well under the 10% target) LEVERS UP; each day = L·base − (L−1)·financing/252.
    base_rows = [(f"d{i}", r) for i, r in enumerate([0.004, -0.004, 0.006, -0.005, 0.003, -0.004] * 30)]
    s = LeveredShadowStrategy(_Base(base_rows), target_vol=0.10, financing_bps=550.0, max_leverage=2.0)
    rows, extra = s.advance(None, {"carry": 1})

    assert extra == {"carry": 1}                                   # extra passes through untouched
    rets = [r for _, r in base_rows]
    lev = min(2.0, 0.10 / _ann_vol(rets))
    lev = max(1.0, lev)
    assert lev > 1.0                                               # a low-vol book levers up
    daily_fin = (550.0 / 1e4) / 252.0 * (lev - 1.0)
    for (d0, r0), (d1, r1) in zip(base_rows, rows):
        assert d0 == d1
        assert abs(r1 - (lev * r0 - daily_fin)) < 1e-12
    # financing makes the levered MEAN strictly below the naive L·mean (leverage is not free)
    assert sum(r for _, r in rows) / len(rows) < lev * (sum(rets) / len(rets))


def test_high_vol_book_is_not_de_levered_below_1x() -> None:
    # A base ALREADY above the vol target: L is floored at 1.0 (a shadow only levers UP) -> no financing, the
    # series is returned UNCHANGED.
    base_rows = [(f"d{i}", r) for i, r in enumerate([0.05, -0.05] * 100)]   # ann vol >> 10%
    s = LeveredShadowStrategy(_Base(base_rows), target_vol=0.10, financing_bps=550.0, max_leverage=2.0)
    rows, _ = s.advance(None, {})
    assert rows == base_rows


def test_leverage_is_capped_at_max_leverage() -> None:
    # A near-zero-vol base would want huge leverage; it is capped at max_leverage.
    base_rows = [(f"d{i}", r) for i, r in enumerate([0.0001, -0.0001] * 100)]
    s = LeveredShadowStrategy(_Base(base_rows), target_vol=0.10, financing_bps=550.0, max_leverage=2.0)
    rows, _ = s.advance(None, {})
    daily_fin = (550.0 / 1e4) / 252.0 * (2.0 - 1.0)               # capped L = 2.0
    assert abs(rows[0][1] - (2.0 * base_rows[0][1] - daily_fin)) < 1e-12


def test_short_series_passes_through_unchanged() -> None:
    # < 2 rows: no vol to compute -> the base series is returned untouched (never a divide-by-zero).
    s = LeveredShadowStrategy(_Base([("d0", 0.01)]), target_vol=0.10, financing_bps=550.0, max_leverage=2.0)
    rows, _ = s.advance(None, {})
    assert rows == [("d0", 0.01)]


def test_financing_sensitivity_band_break_even_and_current_flag() -> None:
    # A low-vol base: levers up; the band shows the levered outcome at each rate. Break-even = the base book's
    # own ann return (above it, leverage subtracts). The `current_bps` row is flagged and included.
    base = [0.004, -0.004, 0.006, -0.005, 0.003, -0.004] * 30
    b = leverage_financing_sensitivity(base, target_vol=0.10, max_leverage=2.0,
                                       rates_bps=(100.0, 300.0, 683.0), current_bps=530.0)
    assert b["leverage"] > 1.0
    # break-even equals the base ann return
    assert abs(b["break_even_pct"] - b["base_ann_return_pct"]) < 1e-9
    # current_bps (530) is present + flagged even though not in rates_bps
    cur = [r for r in b["rows"] if r["is_current"]]
    assert len(cur) == 1 and abs(cur[0]["rate_pct"] - 5.30) < 1e-9
    # higher financing -> lower levered return + Sharpe (monotone)
    rets = [r["ann_return_pct"] for r in b["rows"]]
    shs = [r["sharpe"] for r in b["rows"]]
    assert rets == sorted(rets, reverse=True) and shs == sorted(shs, reverse=True)
    # every row shares the SAME levered vol (= L * base vol) — only the financing drag differs
    assert len({round(r["ann_vol_pct"], 6) for r in b["rows"]}) == 1


def test_financing_sensitivity_marks_rates_above_break_even_as_subtracting() -> None:
    # A base whose ann return is small: a financing rate above it makes leverage SUBTRACT return (adds=False).
    base = [0.0005, -0.0003] * 200          # tiny positive drift -> low break-even
    b = leverage_financing_sensitivity(base, target_vol=0.10, max_leverage=3.0,
                                       rates_bps=(50.0, 2000.0))
    lo, hi = b["rows"][0], b["rows"][-1]
    assert lo["adds"] is True                # 0.5% financing < break-even
    assert hi["adds"] is False               # 20% financing >> break-even -> subtracts
