"""Portfolio netting: multiple sleeves (incl. same market) net into per-market target weights,
capped at the book's gross leverage."""
from __future__ import annotations

import numpy as np

from fxhnt.domain.models import AssetClass, Market, PriceSeries, StrategySpec
from fxhnt.domain.portfolio import Book, StrategyAllocation, compute_target_weights

SPY = Market(symbol="SPY", asset_class=AssetClass.ETF)
QQQ = Market(symbol="QQQ", asset_class=AssetClass.ETF)


def _uptrend(market: Market, n: int = 400) -> PriceSeries:
    close = 100.0 * np.cumprod(1.0 + np.full(n, 0.001))  # monotone up -> trend goes long
    return PriceSeries(market=market, dates=tuple(str(i) for i in range(n)), close=close)


def test_two_sleeves_net_and_respect_leverage_cap() -> None:
    book = Book(
        name="t",
        allocations=[
            StrategyAllocation(spec=StrategySpec(kind="trend", params={"window": 50}), market=SPY, weight=0.7),
            StrategyAllocation(spec=StrategySpec(kind="trend", params={"window": 50}), market=QQQ, weight=0.7),
        ],
        max_gross_leverage=1.0,
    )
    prices = {SPY: _uptrend(SPY), QQQ: _uptrend(QQQ)}
    targets = compute_target_weights(book, prices)
    # both trends are long (+1), weights 0.7+0.7=1.4 gross > 1.0 cap -> scaled to 0.5 each
    assert abs(sum(abs(w) for w in targets.values()) - 1.0) < 1e-9
    assert abs(targets[SPY] - 0.5) < 1e-9 and abs(targets[QQQ] - 0.5) < 1e-9


def test_same_market_sleeves_add() -> None:
    book = Book(
        name="t",
        allocations=[
            StrategyAllocation(spec=StrategySpec(kind="trend", params={"window": 50}), market=SPY, weight=0.3),
            StrategyAllocation(spec=StrategySpec(kind="trend", params={"window": 100}), market=SPY, weight=0.2),
        ],
        max_gross_leverage=2.0,
    )
    targets = compute_target_weights(book, {SPY: _uptrend(SPY)})
    assert abs(targets[SPY] - 0.5) < 1e-9  # 0.3 + 0.2, both long, under the cap
