"""Backtest engine for OpenClaw Trading.

This module provides the BacktestEngine class for historical data backtesting
with event-driven architecture, slippage simulation, and commission calculation.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum, auto
from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List, Optional, Protocol

import numpy as np
import pandas as pd
from pydantic import BaseModel, Field

from openclaw.data.interface import DataSource, Interval
from openclaw.utils.logging import get_logger


class EventType(Enum):
    """Types of backtest events."""

    BAR_OPEN = auto()
    BAR_CLOSE = auto()
    SIGNAL = auto()
    ORDER = auto()
    TRADE = auto()
    END_OF_DATA = auto()


@dataclass
class BacktestEvent:
    """Event in the backtest system.

    Attributes:
        event_type: Type of the event
        timestamp: Event timestamp
        data: Event data payload
    """

    event_type: EventType
    timestamp: datetime
    data: Dict[str, Any] = field(default_factory=dict)


@dataclass
class Position:
    """Trading position.

    Attributes:
        symbol: Trading symbol
        quantity: Number of shares/contracts
        entry_price: Average entry price
        entry_time: Position entry timestamp
        side: "long" or "short"
    """

    symbol: str
    quantity: float
    entry_price: float
    entry_time: datetime
    side: str = "long"

    def market_value(self, current_price: float) -> float:
        """Calculate market value at given price."""
        return self.quantity * current_price

    def unrealized_pnl(self, current_price: float) -> float:
        """Calculate unrealized PnL at given price."""
        if self.side == "long":
            return self.quantity * (current_price - self.entry_price)
        else:
            return self.quantity * (self.entry_price - current_price)


@dataclass
class TradeRecord:
    """Record of a completed trade.

    Attributes:
        symbol: Trading symbol
        entry_time: Entry timestamp
        exit_time: Exit timestamp
        entry_price: Entry price
        exit_price: Exit price
        quantity: Trade quantity
        side: "long" or "short"
        pnl: Profit/loss amount
        commission: Commission paid
        slippage: Slippage cost
    """

    symbol: str
    entry_time: datetime
    exit_time: datetime
    entry_price: float
    exit_price: float
    quantity: float
    side: str
    pnl: float
    commission: float
    slippage: float

    @property
    def total_cost(self) -> float:
        """Total transaction cost."""
        return self.commission + self.slippage

    @property
    def net_pnl(self) -> float:
        """Net PnL after costs."""
        return self.pnl - self.total_cost


class BacktestResult(BaseModel):
    """Comprehensive backtest results.

    Attributes:
        start_date: Backtest start date
        end_date: Backtest end date
        initial_capital: Starting capital
        final_equity: Final equity value
        total_return: Total return percentage
        total_trades: Number of completed trades
        winning_trades: Number of winning trades
        losing_trades: Number of losing trades
        win_rate: Win rate percentage
        avg_win: Average winning trade
        avg_loss: Average losing trade
        profit_factor: Profit factor (gross profit / gross loss)
        sharpe_ratio: Sharpe ratio
        max_drawdown: Maximum drawdown percentage
        max_drawdown_duration: Maximum drawdown duration in days
        volatility: Annualized volatility
        calmar_ratio: Calmar ratio (return / max drawdown)
        equity_curve: List of equity values over time
    """

    start_date: datetime
    end_date: datetime
    initial_capital: float
    final_equity: float
    total_return: float = Field(..., description="Total return as percentage")
    total_trades: int
    winning_trades: int
    losing_trades: int
    win_rate: float = Field(..., description="Win rate as percentage")
    avg_win: float
    avg_loss: float
    profit_factor: float
    sharpe_ratio: float
    max_drawdown: float = Field(..., description="Max drawdown as percentage")
    max_drawdown_duration: int = Field(..., description="Max DD duration in days")
    volatility: float = Field(..., description="Annualized volatility")
    calmar_ratio: float
    equity_curve: List[float] = Field(default_factory=list)
    trades: List[TradeRecord] = Field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        """Convert result to dictionary."""
        return {
            "start_date": self.start_date.isoformat(),
            "end_date": self.end_date.isoformat(),
            "initial_capital": self.initial_capital,
            "final_equity": self.final_equity,
            "total_return": f"{self.total_return:.2f}%",
            "total_trades": self.total_trades,
            "winning_trades": self.winning_trades,
            "losing_trades": self.losing_trades,
            "win_rate": f"{self.win_rate:.2f}%",
            "avg_win": f"${self.avg_win:.2f}",
            "avg_loss": f"${self.avg_loss:.2f}",
            "profit_factor": f"{self.profit_factor:.2f}",
            "sharpe_ratio": f"{self.sharpe_ratio:.2f}",
            "max_drawdown": f"{self.max_drawdown:.2f}%",
            "max_drawdown_duration": f"{self.max_drawdown_duration} days",
            "volatility": f"{self.volatility:.2f}%",
            "calmar_ratio": f"{self.calmar_ratio:.2f}",
        }


class Strategy(Protocol):
    """Protocol for backtest strategies."""

    def on_bar(self, data: pd.Series, context: Dict[str, Any]) -> Optional[str]:
        """Process a new bar of data.

        Args:
            data: Current bar data (OHLCV)
            context: Additional context (positions, equity, etc.)

        Returns:
            Signal string ("buy", "sell", "hold", or None)
        """
        ...

    def on_trade(self, trade: TradeRecord, context: Dict[str, Any]) -> None:
        """Called when a trade is completed.

        Args:
            trade: Completed trade record
            context: Additional context
        """
        ...


class SlippageModel(ABC):
    """Abstract base class for slippage models."""

    @abstractmethod
    def calculate_slippage(
        self,
        price: float,
        quantity: float,
        side: str,
        volatility: float,
        volume: float,
    ) -> float:
        """Calculate slippage for a trade.

        Args:
            price: Intended execution price
            quantity: Trade quantity
            side: "buy" or "sell"
            volatility: Current volatility (e.g., ATR / price)
            volume: Current bar volume

        Returns:
            Slippage amount (absolute value)
        """
        pass


class FixedSlippageModel(SlippageModel):
    """Fixed slippage model - constant amount per trade."""

    def __init__(self, fixed_amount: float = 0.01) -> None:
        """Initialize fixed slippage model.

        Args:
            fixed_amount: Fixed slippage amount per share
        """
        self.fixed_amount = fixed_amount

    def calculate_slippage(
        self,
        price: float,
        quantity: float,
        side: str,
        volatility: float,
        volume: float,
    ) -> float:
        """Calculate fixed slippage."""
        return self.fixed_amount * quantity


class PercentageSlippageModel(SlippageModel):
    """Percentage-based slippage model."""

    def __init__(self, percentage: float = 0.001) -> None:
        """Initialize percentage slippage model.

        Args:
            percentage: Slippage percentage (e.g., 0.001 = 0.1%)
        """
        self.percentage = percentage

    def calculate_slippage(
        self,
        price: float,
        quantity: float,
        side: str,
        volatility: float,
        volume: float,
    ) -> float:
        """Calculate percentage-based slippage."""
        trade_value = price * quantity
        return trade_value * self.percentage


class VolatilitySlippageModel(SlippageModel):
    """Volatility-adjusted slippage model."""

    def __init__(
        self,
        base_percentage: float = 0.0005,
        volatility_multiplier: float = 1.0,
    ) -> None:
        """Initialize volatility slippage model.

        Args:
            base_percentage: Base slippage percentage
            volatility_multiplier: Multiplier for volatility adjustment
        """
        self.base_percentage = base_percentage
        self.volatility_multiplier = volatility_multiplier

    def calculate_slippage(
        self,
        price: float,
        quantity: float,
        side: str,
        volatility: float,
        volume: float,
    ) -> float:
        """Calculate volatility-adjusted slippage."""
        trade_value = price * quantity
        adjusted_percentage = self.base_percentage * (
            1 + volatility * self.volatility_multiplier
        )
        return trade_value * min(adjusted_percentage, 0.01)  # Cap at 1%


class CommissionModel(ABC):
    """Abstract base class for commission models."""

    @abstractmethod
    def calculate_commission(
        self,
        price: float,
        quantity: float,
    ) -> float:
        """Calculate commission for a trade.

        Args:
            price: Trade price
            quantity: Trade quantity

        Returns:
            Commission amount
        """
        pass


class FixedCommissionModel(CommissionModel):
    """Fixed commission per trade."""

    def __init__(self, fixed_amount: float = 5.0) -> None:
        """Initialize fixed commission model.

        Args:
            fixed_amount: Fixed commission per trade
        """
        self.fixed_amount = fixed_amount

    def calculate_commission(self, price: float, quantity: float) -> float:
        """Calculate fixed commission."""
        return self.fixed_amount


class PercentageCommissionModel(CommissionModel):
    """Percentage-based commission model."""

    def __init__(
        self,
        percentage: float = 0.001,
        min_commission: float = 1.0,
        max_commission: Optional[float] = None,
    ) -> None:
        """Initialize percentage commission model.

        Args:
            percentage: Commission percentage (e.g., 0.001 = 0.1%)
            min_commission: Minimum commission per trade
            max_commission: Maximum commission per trade (optional)
        """
        self.percentage = percentage
        self.min_commission = min_commission
        self.max_commission = max_commission

    def calculate_commission(self, price: float, quantity: float) -> float:
        """Calculate percentage-based commission."""
        trade_value = price * quantity
        commission = trade_value * self.percentage

        if self.min_commission:
            commission = max(commission, self.min_commission)
        if self.max_commission:
            commission = min(commission, self.max_commission)

        return commission


class TieredCommissionModel(CommissionModel):
    """Tiered commission based on trade value."""

    def __init__(
        self,
        tiers: Optional[List[tuple[float, float]]] = None,
    ) -> None:
        """Initialize tiered commission model.

        Args:
            tiers: List of (threshold, percentage) tuples
        """
        self.tiers = tiers or [
            (0, 0.002),  # 0.2% for trades under $10k
            (10000, 0.0015),  # 0.15% for $10k-$50k
            (50000, 0.001),  # 0.1% for $50k+
        ]

    def calculate_commission(self, price: float, quantity: float) -> float:
        """Calculate tiered commission."""
        trade_value = price * quantity

        applicable_rate = self.tiers[0][1]
        for threshold, rate in self.tiers:
            if trade_value >= threshold:
                applicable_rate = rate

        return trade_value * applicable_rate


from abc import ABC, abstractmethod


class BacktestEngine:
    """Historical data backtesting engine.

    This engine provides event-driven backtesting with slippage simulation,
    commission calculation, and comprehensive performance reporting.

    Args:
        initial_capital: Starting capital for the backtest
        start_date: Backtest start date
        end_date: Backtest end date
        slippage_model: Slippage model to use (default: PercentageSlippageModel)
        commission_model: Commission model to use (default: PercentageCommissionModel)
    """

    def __init__(
        self,
        initial_capital: float,
        start_date: datetime,
        end_date: datetime,
        slippage_model: Optional[SlippageModel] = None,
        commission_model: Optional[CommissionModel] = None,
    ):
        self.initial_capital = initial_capital
        self.current_equity = initial_capital
        self.start_date = start_date
        self.end_date = end_date

        # Initialize models
        self.slippage_model = slippage_model or PercentageSlippageModel(0.001)
        self.commission_model = commission_model or PercentageCommissionModel(0.001)

        # State tracking
        self.positions: Dict[str, Position] = {}
        self.trades: List[TradeRecord] = []
        self.equity_curve: List[float] = [initial_capital]
        self.equity_timestamps: List[datetime] = []

        # Event system
        self._event_handlers: Dict[EventType, List[Callable]] = {
            event_type: [] for event_type in EventType
        }

        # Data storage
        self._data: Optional[pd.DataFrame] = None
        self._current_bar: Optional[pd.Series] = None
        self._current_index: int = 0

        self.logger = get_logger("backtest.engine")

        self.logger.info(
            f"BacktestEngine initialized: ${initial_capital:,.2f} capital, "
            f"{start_date.date()} to {end_date.date()}"
        )

    def register_event_handler(
        self,
        event_type: EventType,
        handler: Callable[[BacktestEvent], None],
    ) -> None:
        """Register an event handler.

        Args:
            event_type: Type of event to handle
            handler: Callback function for the event
        """
        self._event_handlers[event_type].append(handler)

    def unregister_event_handler(
        self,
        event_type: EventType,
        handler: Callable[[BacktestEvent], None],
    ) -> None:
        """Unregister an event handler."""
        if handler in self._event_handlers[event_type]:
            self._event_handlers[event_type].remove(handler)

    def _emit_event(self, event: BacktestEvent) -> None:
        """Emit an event to all registered handlers."""
        for handler in self._event_handlers.get(event.event_type, []):
            try:
                handler(event)
            except Exception as e:
                self.logger.error(f"Event handler error: {e}")

    def load_data(
        self,
        symbol: str,
        source: DataSource,
        interval: Interval = Interval.DAY_1,
    ) -> pd.DataFrame:
        """Load historical data for backtesting.

        Args:
            symbol: Trading symbol
            source: Data source to fetch from
            interval: Data interval

        Returns:
            DataFrame with OHLCV data
        """
        import asyncio

        self.logger.info(f"Loading data for {symbol} from {source.name}")

        # Run async fetch in sync context
        loop = asyncio.new_event_loop()
        try:
            df = loop.run_until_complete(
                source.fetch_ohlcv(
                    symbol=symbol,
                    interval=interval,
                    start=self.start_date,
                    end=self.end_date,
                )
            )
        finally:
            loop.close()

        # Filter by date range
        df["timestamp"] = pd.to_datetime(df["timestamp"])
        df = df[(df["timestamp"] >= self.start_date) & (df["timestamp"] <= self.end_date)]
        df = df.sort_values("timestamp").reset_index(drop=True)

        self._data = df
        self.logger.info(f"Loaded {len(df)} bars of data for {symbol}")

        return df.copy()

    def run(self, strategy: Strategy) -> BacktestResult:
        """Run the backtest with the given strategy.

        Args:
            strategy: Trading strategy to backtest

        Returns:
            BacktestResult with comprehensive performance metrics
        """
        if self._data is None or self._data.empty:
            raise ValueError("No data loaded. Call load_data() before run().")

        self.logger.info("Starting backtest...")

        # Reset state
        self.current_equity = self.initial_capital
        self.positions.clear()
        self.trades.clear()
        self.equity_curve = [self.initial_capital]
        self.equity_timestamps = []

        # Calculate volatility for slippage model
        self._calculate_volatility()

        # Process each bar
        for idx, bar in self._data.iterrows():
            self._current_bar = bar
            self._current_index = idx

            # Emit bar open event
            self._emit_event(
                BacktestEvent(
                    event_type=EventType.BAR_OPEN,
                    timestamp=bar["timestamp"],
                    data={"bar": bar.to_dict()},
                )
            )

            # Get strategy signal
            context = self._build_context()
            signal = strategy.on_bar(bar, context)

            if signal:
                signal = signal.lower()
                self._emit_event(
                    BacktestEvent(
                        event_type=EventType.SIGNAL,
                        timestamp=bar["timestamp"],
                        data={"signal": signal, "bar": bar.to_dict()},
                    )
                )

                # Execute based on signal
                symbol = bar.get("symbol", "UNKNOWN")
                if signal == "buy":
                    self._execute_buy(symbol, bar)
                elif signal == "sell":
                    self._execute_sell(symbol, bar)

            # Update equity curve
            self._update_equity(bar)

            # Emit bar close event
            self._emit_event(
                BacktestEvent(
                    event_type=EventType.BAR_CLOSE,
                    timestamp=bar["timestamp"],
                    data={"equity": self.current_equity},
                )
            )

        # Close any remaining positions at the end
        if self._data is not None:
            final_bar = self._data.iloc[-1]
            self._close_all_positions(final_bar)

        # Emit end of data event
        self._emit_event(
            BacktestEvent(
                event_type=EventType.END_OF_DATA,
                timestamp=self._data.iloc[-1]["timestamp"],
                data={"final_equity": self.current_equity},
            )
        )

        # Generate results
        result = self._generate_result()
        self.logger.info(f"Backtest complete: {result.total_return:.2f}% return")

        return result

    def _calculate_volatility(self) -> None:
        """Calculate volatility metrics for slippage model."""
        if self._data is None or len(self._data) < 2:
            return

        # Calculate ATR-based volatility
        high_low = self._data["high"] - self._data["low"]
        high_close = np.abs(self._data["high"] - self._data["close"].shift())
        low_close = np.abs(self._data["low"] - self._data["close"].shift())

        tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
        atr = tr.rolling(window=14).mean()

        self._data["volatility"] = (atr / self._data["close"]).fillna(0.01)
        self._data["atr"] = atr.fillna(0)

    def _build_context(self) -> Dict[str, Any]:
        """Build context dictionary for strategy."""
        return {
            "equity": self.current_equity,
            "positions": self.positions.copy(),
            "trades": self.trades.copy(),
            "equity_curve": self.equity_curve.copy(),
            "bar_index": self._current_index,
        }

    def _execute_buy(self, symbol: str, bar: pd.Series) -> None:
        """Execute a buy order."""
        # Close any existing short position
        if symbol in self.positions and self.positions[symbol].side == "short":
            self._close_position(symbol, bar)

        # Calculate position size (simplified: use 10% of equity)
        position_size = self.current_equity * 0.1
        price = bar["close"]
        quantity = position_size / price

        # Calculate costs
        volatility = bar.get("volatility", 0.01)
        volume = bar.get("volume", 0)

        slippage = self.slippage_model.calculate_slippage(
            price=price,
            quantity=quantity,
            side="buy",
            volatility=volatility,
            volume=volume,
        )

        commission = self.commission_model.calculate_commission(price, quantity)

        # Adjust price for slippage (buy at higher price)
        executed_price = price + (slippage / quantity)

        # Create position
        self.positions[symbol] = Position(
            symbol=symbol,
            quantity=quantity,
            entry_price=executed_price,
            entry_time=bar["timestamp"],
            side="long",
        )

        # Deduct commission from equity
        self.current_equity -= commission + slippage

        self.logger.debug(
            f"BUY {symbol}: {quantity:.2f} @ ${executed_price:.2f} "
            f"(commission: ${commission:.2f}, slippage: ${slippage:.2f})"
        )

        self._emit_event(
            BacktestEvent(
                event_type=EventType.ORDER,
                timestamp=bar["timestamp"],
                data={
                    "side": "buy",
                    "symbol": symbol,
                    "quantity": quantity,
                    "price": executed_price,
                    "commission": commission,
                    "slippage": slippage,
                },
            )
        )

    def _execute_sell(self, symbol: str, bar: pd.Series) -> None:
        """Execute a sell order."""
        # Close any existing long position
        if symbol in self.positions and self.positions[symbol].side == "long":
            self._close_position(symbol, bar)
        else:
            # Open short position (simplified)
            position_size = self.current_equity * 0.1
            price = bar["close"]
            quantity = position_size / price

            volatility = bar.get("volatility", 0.01)
            volume = bar.get("volume", 0)

            slippage = self.slippage_model.calculate_slippage(
                price=price,
                quantity=quantity,
                side="sell",
                volatility=volatility,
                volume=volume,
            )

            commission = self.commission_model.calculate_commission(price, quantity)
            executed_price = price - (slippage / quantity)

            self.positions[symbol] = Position(
                symbol=symbol,
                quantity=quantity,
                entry_price=executed_price,
                entry_time=bar["timestamp"],
                side="short",
            )

            self.current_equity -= commission + slippage

    def _close_position(self, symbol: str, bar: pd.Series) -> Optional[TradeRecord]:
        """Close an existing position and record the trade."""
        if symbol not in self.positions:
            return None

        position = self.positions[symbol]
        exit_price = bar["close"]
        exit_time = bar["timestamp"]

        # Calculate costs
        volatility = bar.get("volatility", 0.01)
        volume = bar.get("volume", 0)

        slippage = self.slippage_model.calculate_slippage(
            price=exit_price,
            quantity=position.quantity,
            side="sell" if position.side == "long" else "buy",
            volatility=volatility,
            volume=volume,
        )

        commission = self.commission_model.calculate_commission(exit_price, position.quantity)

        # Adjust exit price for slippage
        if position.side == "long":
            executed_exit_price = exit_price - (slippage / position.quantity)
            pnl = position.quantity * (executed_exit_price - position.entry_price)
        else:
            executed_exit_price = exit_price + (slippage / position.quantity)
            pnl = position.quantity * (position.entry_price - executed_exit_price)

        # Create trade record
        trade = TradeRecord(
            symbol=symbol,
            entry_time=position.entry_time,
            exit_time=exit_time,
            entry_price=position.entry_price,
            exit_price=executed_exit_price,
            quantity=position.quantity,
            side=position.side,
            pnl=pnl,
            commission=commission,
            slippage=slippage,
        )

        self.trades.append(trade)
        del self.positions[symbol]

        # Update equity
        self.current_equity += pnl - commission - slippage

        self.logger.debug(
            f"CLOSE {symbol}: PnL=${pnl:.2f}, "
            f"commission=${commission:.2f}, slippage=${slippage:.2f}"
        )

        self._emit_event(
            BacktestEvent(
                event_type=EventType.TRADE,
                timestamp=exit_time,
                data={"trade": trade},
            )
        )

        return trade

    def _close_all_positions(self, bar: pd.Series) -> None:
        """Close all open positions."""
        symbols = list(self.positions.keys())
        for symbol in symbols:
            self._close_position(symbol, bar)

    def _update_equity(self, bar: pd.Series) -> None:
        """Update equity curve with current positions."""
        unrealized_pnl = 0.0
        current_price = bar["close"]

        for position in self.positions.values():
            if position.side == "long":
                unrealized_pnl += position.quantity * (current_price - position.entry_price)
            else:
                unrealized_pnl += position.quantity * (position.entry_price - current_price)

        total_equity = self.current_equity + unrealized_pnl
        self.equity_curve.append(total_equity)
        self.equity_timestamps.append(bar["timestamp"])

    def _generate_result(self) -> BacktestResult:
        """Generate comprehensive backtest results."""
        if not self.equity_curve:
            raise ValueError("No equity data available")

        final_equity = self.equity_curve[-1]
        total_return = (final_equity - self.initial_capital) / self.initial_capital * 100

        # Trade statistics
        total_trades = len(self.trades)
        winning_trades = [t for t in self.trades if t.net_pnl > 0]
        losing_trades = [t for t in self.trades if t.net_pnl <= 0]

        win_count = len(winning_trades)
        loss_count = len(losing_trades)
        win_rate = (win_count / total_trades * 100) if total_trades > 0 else 0.0

        avg_win = np.mean([t.net_pnl for t in winning_trades]) if winning_trades else 0.0
        avg_loss = np.mean([t.net_pnl for t in losing_trades]) if losing_trades else 0.0

        gross_profit = sum(t.net_pnl for t in winning_trades)
        gross_loss = abs(sum(t.net_pnl for t in losing_trades))
        profit_factor = (
            gross_profit / gross_loss if gross_loss > 0 else float("inf")
        )

        # Calculate returns series for Sharpe
        returns = pd.Series(self.equity_curve).pct_change().dropna()

        # Sharpe ratio (annualized, assuming 252 trading days)
        if len(returns) > 1 and returns.std() > 0:
            sharpe_ratio = (returns.mean() / returns.std()) * np.sqrt(252)
        else:
            sharpe_ratio = 0.0

        # Volatility (annualized)
        volatility = returns.std() * np.sqrt(252) * 100 if len(returns) > 1 else 0.0

        # Maximum drawdown
        max_drawdown, max_dd_duration = self._calculate_max_drawdown()

        # Calmar ratio
        calmar_ratio = (
            total_return / max_drawdown if max_drawdown > 0 else float("inf")
        )

        return BacktestResult(
            start_date=self.start_date,
            end_date=self.end_date,
            initial_capital=self.initial_capital,
            final_equity=final_equity,
            total_return=total_return,
            total_trades=total_trades,
            winning_trades=win_count,
            losing_trades=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,
            max_drawdown_duration=max_dd_duration,
            volatility=volatility,
            calmar_ratio=calmar_ratio,
            equity_curve=self.equity_curve.copy(),
            trades=self.trades.copy(),
        )

    def _calculate_max_drawdown(self) -> tuple[float, int]:
        """Calculate maximum drawdown and its duration.

        Returns:
            Tuple of (max_drawdown_percentage, max_duration_days)
        """
        if not self.equity_curve:
            return 0.0, 0

        equity_series = pd.Series(self.equity_curve)
        rolling_max = equity_series.cummax()
        drawdown = (equity_series - rolling_max) / rolling_max * 100

        max_drawdown = abs(drawdown.min())

        # Calculate max drawdown duration
        max_duration = 0
        current_duration = 0
        in_drawdown = False

        for dd in drawdown:
            if dd < 0:
                if not in_drawdown:
                    in_drawdown = True
                    current_duration = 1
                else:
                    current_duration += 1
            else:
                if in_drawdown:
                    in_drawdown = False
                    max_duration = max(max_duration, current_duration)
                    current_duration = 0

        # If still in drawdown at the end
        if in_drawdown:
            max_duration = max(max_duration, current_duration)

        return max_drawdown, max_duration

    def get_results(self) -> BacktestResult:
        """Get the current backtest results.

        Returns:
            BacktestResult with current performance metrics
        """
        return self._generate_result()

    def reset(self) -> None:
        """Reset the engine to initial state."""
        self.current_equity = self.initial_capital
        self.positions.clear()
        self.trades.clear()
        self.equity_curve = [self.initial_capital]
        self.equity_timestamps.clear()
        self._current_bar = None
        self._current_index = 0

        self.logger.info("BacktestEngine reset")
