"""
Base strategy class for all trading strategies.

Strategies should inherit from BaseStrategy and implement the run() method.
"""
from abc import ABC, abstractmethod

import pandas as pd

from engine.market import MarketType


class BaseStrategy(ABC):
    """
    Abstract base class for trading strategies.
    
    Class Attributes:
        default_market_type: Default market type for this strategy
        default_leverage: Default leverage (only applies to perpetuals)
        default_sl_stop: Default stop-loss percentage
        default_tp_stop: Default take-profit percentage
        default_sl_trail: Whether stop-loss is trailing by default
    """
    # Market configuration defaults
    default_market_type: MarketType = MarketType.SPOT
    default_leverage: int = 1
    
    # Risk management defaults (can be overridden per strategy)
    default_sl_stop: float | None = None
    default_tp_stop: float | None = None
    default_sl_trail: bool = False
    
    def __init__(self, **kwargs):
        self.params = kwargs

    @abstractmethod
    def run(
        self, 
        close: pd.Series, 
        **kwargs
    ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame]:
        """
        Run the strategy logic.
        
        Args:
            close: Price series (can be multiple columns for grid search)
            **kwargs: Additional data (high, low, open, volume) and parameters
            
        Returns:
            Tuple of 4 DataFrames/Series:
                - long_entries: Boolean signals to open long positions
                - long_exits: Boolean signals to close long positions
                - short_entries: Boolean signals to open short positions
                - short_exits: Boolean signals to close short positions
                
        Note:
            For spot markets, short signals will be ignored.
            For backward compatibility, strategies can return 2-tuple (entries, exits)
            which will be interpreted as long-only signals.
        """
        pass

    def get_indicator(self, ind_cls, *args, **kwargs):
        """Helper to run a vectorbt indicator."""
        return ind_cls.run(*args, **kwargs)
    
    @staticmethod
    def create_empty_signals(reference: pd.Series | pd.DataFrame) -> pd.DataFrame:
        """
        Create an empty (all False) signal DataFrame matching the reference shape.
        
        Args:
            reference: Series or DataFrame to match shape/index
            
        Returns:
            DataFrame of False values with same shape as reference
        """
        if isinstance(reference, pd.DataFrame):
            return pd.DataFrame(False, index=reference.index, columns=reference.columns)
        return pd.Series(False, index=reference.index)