"""
Trading Helpers

HasBarIndex protocol and TradingHelpers class that delegates to submodule functions.

Usage:
    from sixtysix import TradingHelpers
"""

from __future__ import annotations
from typing import Optional, Protocol, Tuple, Union, runtime_checkable
import numpy as np
import pandas as pd


@runtime_checkable
class HasBarIndex(Protocol):
    """Protocol for objects that have a bar_index attribute."""
    @property
    def bar_index(self) -> int: ...


class TradingHelpers:
    """
    Helper class providing PineScript-like technical analysis functions.

    Wraps trading utilities (crossover, crossunder, pivothigh, etc.) to use
    the parent's context automatically. Also provides shared TA computation
    methods (atr, rsi, adx, macd, ema, sma) to eliminate duplicated math
    across strategies.

    Usage in Strategy:
        def on_bar(self, df):
            if self.ta.crossover(self.fast_sma, self.slow_sma):
                return self.buy(reason='Golden cross')

            if self.ta.crossunder(self.fast_sma, self.slow_sma):
                return self.close(reason='Death cross')

            ph = self.ta.pivothigh(df['high'], 10, 10)
            if ph is not None:
                self.last_high = ph

        @computed
        def my_atr(self, df):
            return self.ta.atr(df, period=14)
    """

    def __init__(self, context_provider: HasBarIndex):
        """
        Args:
            context_provider: Object that provides bar_index (Strategy or TradingContext)
        """
        self._ctx = context_provider

    # -- Shared TA Computation Methods (delegated to indicators module) --

    @staticmethod
    def rma(series: pd.Series, period: int) -> pd.Series:
        """Wilder's RMA — exact match for PineScript ta.rma()."""
        from .indicators import rma
        return rma(series, period)

    @staticmethod
    def sma(series: pd.Series, period: int) -> pd.Series:
        """Simple Moving Average."""
        from .indicators import sma
        return sma(series, period)

    @staticmethod
    def ema(series: pd.Series, period: int) -> pd.Series:
        """Exponential Moving Average (span-based, matches pandas default)."""
        from .indicators import ema
        return ema(series, period)

    @staticmethod
    def atr(df: pd.DataFrame, period: int = 14) -> pd.Series:
        """Average True Range — exact match for PineScript ta.atr()."""
        from .indicators import atr
        return atr(df, period)

    @staticmethod
    def rsi(df: pd.DataFrame, period: int = 14) -> pd.Series:
        """Relative Strength Index — exact match for PineScript ta.rsi()."""
        from .indicators import rsi
        return rsi(df, period)

    @staticmethod
    def dmi(df: pd.DataFrame, di_period: int = 14, adx_period: int = 14):
        """Directional Movement Index — exact match for PineScript ta.dmi()."""
        from .indicators import dmi
        return dmi(df, di_period, adx_period)

    @staticmethod
    def adx(df: pd.DataFrame, period: int = 14) -> pd.Series:
        """Average Directional Index — convenience wrapper around dmi()."""
        from .indicators import adx
        return adx(df, period)

    @staticmethod
    def macd(df: pd.DataFrame, fast: int = 12, slow: int = 26, signal: int = 9) -> pd.DataFrame:
        """MACD indicator."""
        from .indicators import macd
        return macd(df, fast, slow, signal)

    # -- Crossover Methods (delegated to crossovers module) --

    def crossover(self, a: pd.Series, b: Union[pd.Series, float]) -> bool:
        """Check if series 'a' crosses above series/value 'b' at the current bar."""
        from .crossovers import crossover
        return crossover(self._ctx, a, b)

    def crossunder(self, a: pd.Series, b: Union[pd.Series, float]) -> bool:
        """Check if series 'a' crosses below series/value 'b' at the current bar."""
        from .crossovers import crossunder
        return crossunder(self._ctx, a, b)

    def crossed(self, a: pd.Series, b: Union[pd.Series, float]) -> bool:
        """Check if series 'a' crossed series/value 'b' in either direction."""
        from .crossovers import crossed
        return crossed(self._ctx, a, b)

    # -- Pivot Methods (delegated to pivots module) --

    def pivothigh(self, source: pd.Series, left: int, right: int) -> Optional[float]:
        """Detect pivot high at confirmation bar."""
        from .pivots import pivothigh
        return pivothigh(self._ctx, source, left, right)

    def pivotlow(self, source: pd.Series, left: int, right: int) -> Optional[float]:
        """Detect pivot low at confirmation bar."""
        from .pivots import pivotlow
        return pivotlow(self._ctx, source, left, right)

    # -- Vectorized Pivot Detection (static, no bar context needed) --

    @staticmethod
    def detect_pivots(high: np.ndarray, low: np.ndarray, length: int) -> Tuple[np.ndarray, np.ndarray]:
        """Detect all pivot highs and lows in a price series (vectorized)."""
        from .pivots import detect_pivots
        return detect_pivots(high, low, length)

    @staticmethod
    def detect_pivot_lows(low: np.ndarray, length: int) -> np.ndarray:
        """Vectorized pivot low detection. Returns boolean mask."""
        from .pivots import detect_pivot_lows
        return detect_pivot_lows(low, length)

    @staticmethod
    def detect_pivot_highs(high: np.ndarray, length: int) -> np.ndarray:
        """Vectorized pivot high detection. Returns boolean mask."""
        from .pivots import detect_pivot_highs
        return detect_pivot_highs(high, length)

    @staticmethod
    def pivot_structure(high: np.ndarray, low: np.ndarray, lookback: int = 30) -> dict:
        """Full pivot structure: higher lows/highs, consecutive counts, distances."""
        from .pivots import pivot_structure
        return pivot_structure(high, low, lookback)

    # -- Additional Indicators (static) --

    @staticmethod
    def bbands(df: pd.DataFrame, period: int = 20, std_dev: float = 2.0) -> pd.DataFrame:
        """Bollinger Bands. Returns DataFrame with upper, middle, lower, bandwidth, pctb."""
        from .indicators import bbands
        return bbands(df, period, std_dev)

    @staticmethod
    def supertrend(df: pd.DataFrame, period: int = 10, multiplier: float = 3.0) -> pd.DataFrame:
        """Supertrend indicator. Returns DataFrame with supertrend, direction."""
        from .indicators import supertrend
        return supertrend(df, period, multiplier)

    @staticmethod
    def renko(
        df: pd.DataFrame,
        brick_size: Optional[float] = None,
        atr_period: int = 14,
        method: str = 'atr',
        pct: Optional[float] = None,
    ) -> pd.DataFrame:
        """Renko overlay. Returns DataFrame with trend, top, bottom."""
        from .indicators import renko
        return renko(df, brick_size, atr_period, method, pct)
