"""Performance tracker for the autonomous trading engine.

Pure computation module that computes portfolio-wide performance metrics
from closed trades and portfolio state data.
"""

from __future__ import annotations

import math

from services.trading.models import ClosedTrade, PerformanceMetrics


class PerformanceComputer:
    """Computes portfolio performance metrics from trade data.

    All methods are pure computations with no side effects or I/O.
    """

    def compute_metrics(
        self,
        closed_trades: list[ClosedTrade],
        portfolio_value: float,
        active_pool: float,
        reserve_pool: float,
        daily_pnl: float,
        unrealized_pnl: float,
        portfolio_heat: float,
        daily_returns: list[float],
    ) -> PerformanceMetrics:
        """Compute all performance metrics from trade data and portfolio state.

        Args:
            closed_trades: List of completed trades.
            portfolio_value: Current total portfolio value.
            active_pool: Current active pool value.
            reserve_pool: Current reserve pool balance.
            daily_pnl: Today's P&L.
            unrealized_pnl: Unrealized P&L across open positions.
            portfolio_heat: Current portfolio heat value.
            daily_returns: List of daily return percentages for Sharpe/drawdown.

        Returns:
            PerformanceMetrics with all computed fields.
        """
        wins = [t for t in closed_trades if t.pnl > 0]
        losses = [t for t in closed_trades if t.pnl <= 0]

        win_count = len(wins)
        loss_count = len(losses)
        total_trades = len(closed_trades)

        win_rate = win_count / total_trades if total_trades > 0 else 0.0

        avg_win = (
            sum(t.pnl for t in wins) / win_count if win_count > 0 else 0.0
        )
        avg_loss = (
            sum(t.pnl for t in losses) / loss_count if loss_count > 0 else 0.0
        )

        gross_profits = sum(t.pnl for t in wins)
        gross_losses = abs(sum(t.pnl for t in losses))

        if gross_losses > 0:
            profit_factor = gross_profits / gross_losses
        else:
            profit_factor = float("inf") if gross_profits > 0 else 0.0

        realized_pnl = sum(t.pnl for t in closed_trades)

        sharpe_ratio = self._compute_sharpe_ratio(daily_returns)
        max_drawdown = self._compute_max_drawdown(daily_returns)
        current_drawdown_pct = self._compute_current_drawdown(daily_returns)

        return PerformanceMetrics(
            total_portfolio_value=portfolio_value,
            active_pool=active_pool,
            reserve_pool=reserve_pool,
            unrealized_pnl=unrealized_pnl,
            realized_pnl=realized_pnl,
            daily_pnl=daily_pnl,
            win_count=win_count,
            loss_count=loss_count,
            win_rate=win_rate,
            avg_win=avg_win,
            avg_loss=avg_loss,
            profit_factor=profit_factor,
            sharpe_ratio=sharpe_ratio,
            max_drawdown=max_drawdown,
            current_drawdown_pct=current_drawdown_pct,
            portfolio_heat=portfolio_heat,
        )

    def compute_trade_metrics(self, trade: ClosedTrade) -> dict:
        """Compute per-trade metrics for a single closed trade.

        Args:
            trade: A completed trade.

        Returns:
            Dictionary with per-trade metrics.
        """
        return {
            "ticker": trade.ticker,
            "entry_price": trade.entry_price,
            "exit_price": trade.exit_price,
            "quantity": trade.quantity,
            "pnl": trade.pnl,
            "pnl_pct": trade.pnl_pct,
            "hold_duration": str(trade.hold_duration),
            "recommendation_id": trade.recommendation_id,
            "is_micro_trade": trade.is_micro_trade,
            "is_win": trade.pnl > 0,
        }

    def filter_by_micro_trade(
        self,
        trades: list[ClosedTrade],
        is_micro: bool,
    ) -> list[ClosedTrade]:
        """Filter trades by micro-trade flag.

        Args:
            trades: List of closed trades.
            is_micro: If True, return only micro-trades; if False, only standard.

        Returns:
            Filtered list of trades.
        """
        return [t for t in trades if t.is_micro_trade == is_micro]

    @staticmethod
    def _compute_sharpe_ratio(daily_returns: list[float]) -> float:
        """Compute annualized Sharpe ratio from daily returns.

        Formula: (mean_daily_return / std_daily_return) * sqrt(252)
        Returns 0.0 if fewer than 2 data points or std is 0.
        """
        if len(daily_returns) < 2:
            return 0.0

        n = len(daily_returns)
        mean_return = sum(daily_returns) / n
        variance = sum((r - mean_return) ** 2 for r in daily_returns) / (n - 1)
        std_return = math.sqrt(variance)

        if std_return < 1e-12:
            return 0.0

        return (mean_return / std_return) * math.sqrt(252)

    @staticmethod
    def _compute_max_drawdown(daily_returns: list[float]) -> float:
        """Compute maximum drawdown from daily returns.

        Tracks cumulative returns and finds the largest peak-to-trough decline.
        Returns 0.0 if no drawdown or insufficient data.
        """
        if not daily_returns:
            return 0.0

        cumulative = 1.0
        peak = 1.0
        max_dd = 0.0

        for r in daily_returns:
            cumulative *= (1.0 + r)
            if cumulative > peak:
                peak = cumulative
            drawdown = (peak - cumulative) / peak if peak > 0 else 0.0
            if drawdown > max_dd:
                max_dd = drawdown

        return max_dd

    @staticmethod
    def _compute_current_drawdown(daily_returns: list[float]) -> float:
        """Compute current drawdown percentage from daily returns.

        Returns the drawdown from the most recent peak to the current value.
        """
        if not daily_returns:
            return 0.0

        cumulative = 1.0
        peak = 1.0

        for r in daily_returns:
            cumulative *= (1.0 + r)
            if cumulative > peak:
                peak = cumulative

        if peak <= 0:
            return 0.0

        return (peak - cumulative) / peak
