"""Pure backtest engine — signal → net returns + stats. Deterministic, no I/O.

Applies a round-trip cost model on turnover so a strategy that only works gross is exposed here.
"""
from __future__ import annotations

import math

import numpy as np

from fxhnt.domain.models import BacktestResult, BacktestStats, PriceSeries, StrategySpec
from fxhnt.domain.strategies.base import get_strategy


def compute_stats(returns: np.ndarray, periods_per_year: int = 252) -> BacktestStats:
    r = np.asarray(returns, float)
    r = r[np.isfinite(r)]
    if len(r) < 2:
        return BacktestStats(cagr=0.0, ann_vol=0.0, sharpe=0.0, max_drawdown=0.0, n_obs=len(r))
    ann, vol = float(r.mean() * periods_per_year), float(r.std() * math.sqrt(periods_per_year))
    eq = np.cumprod(1.0 + r)
    years = len(r) / periods_per_year
    cagr = float(eq[-1] ** (1.0 / years) - 1.0) if years > 0 and eq[-1] > 0 else 0.0
    dd = float((eq / np.maximum.accumulate(eq) - 1.0).min())
    return BacktestStats(cagr=cagr, ann_vol=vol, sharpe=(ann / vol if vol > 0 else 0.0),
                         max_drawdown=dd, n_obs=len(r))


def run_backtest(prices: PriceSeries, spec: StrategySpec, cost_bps_per_turnover: float = 10.0) -> BacktestResult:
    strategy = get_strategy(spec.kind)
    pos = strategy.positions(prices, spec.params)
    ret = prices.returns()
    gross = pos * ret
    turnover = np.abs(np.diff(np.concatenate([[0.0], pos])))
    cost = turnover * (cost_bps_per_turnover / 1e4)
    net = gross - cost
    return BacktestResult(spec=spec, market=prices.market, returns=net, stats=compute_stats(net))
