"""
策略建構器（主入口類別）

管理整體策略建構流程：
1. 自動繼承 base_instrument 和 base_freq
2. 添加指標（回傳 IndicatorSpec）
3. 設定 base blueprint
4. 添加 advanced blueprints（鏈式）
5. 建構最終 StrategyConfig

Usage:
    from tradepose_client.builder import StrategyBuilder, BlueprintBuilder

    # 1. 建立 builder
    builder = StrategyBuilder(
        name="KOG_US100_15T_ST_21_3",
        base_instrument="US100.cash_M15_FTMO_FUTURE",
        base_freq="15min"
    )

    # 2. 添加指標（自動繼承 instrument_id）
    from tradepose_models.enums import IndicatorType, Freq

    atr = builder.add_indicator(IndicatorType.ATR, period=21, freq=Freq.DAY_1, shift=1)
    st = builder.add_indicator(
        IndicatorType.SUPERTREND,
        multiplier=3.0,
        volatility_column=atr.display_name(),
        freq=Freq.DAY_1,
        shift=1
    )

    # 3. 建立並設定 base blueprint
    base_bp = BlueprintBuilder("base", "Long", "Trend")\
        .add_entry_trigger(...)\
        .add_exit_trigger(...)\
        .build()

    builder.set_base_blueprint(base_bp)

    # 4. 添加 advanced blueprints
    adv_bp = BlueprintBuilder("risk_mgmt", "Long", "Trend")\
        .add_exit_trigger(...)\
        .build()

    builder.add_advanced_blueprint(adv_bp)

    # 5. 建構最終策略
    strategy = builder.build(
        volatility_indicator=atr,
        note="US100 順勢策略"
    )

    # 6. 儲存或註冊
    strategy.save("strategy.json")
"""

from typing import TYPE_CHECKING, List, Optional, Union

if TYPE_CHECKING:
    import polars as pl
    from tradepose_models.enums import Freq, IndicatorType
    from tradepose_models.strategy import Blueprint, DataSource, IndicatorSpec, StrategyConfig
    from tradepose_models.strategy.params import StrategyParams

_FREQ_SECONDS: dict[str, int] = {
    "1min": 60,
    "5min": 300,
    "15min": 900,
    "30min": 1800,
    "1h": 3600,
    "4h": 14400,
    "1D": 86400,
    "1W": 604800,
    "1M": 2592000,
}


def _freq_to_seconds(freq: Union[str, "Freq"]) -> int:
    key = freq.value if hasattr(freq, "value") else freq
    return _FREQ_SECONDS[key]


class StrategyBuilder:
    """
    策略建構器（主入口類別）

    管理整體策略建構流程，自動繼承 base_instrument 和 base_freq。

    Attributes:
        name (str): 策略名稱
        base_instrument (str): 基準商品
        base_freq (Freq): 基準頻率

    Methods:
        add_indicator(indicator_type, **kwargs) -> IndicatorSpec:
            添加指標（自動繼承 instrument_id）
        set_base_blueprint(blueprint) -> StrategyBuilder:
            設定 Base Blueprint
        add_advanced_blueprint(blueprint) -> StrategyBuilder:
            添加 Advanced Blueprint（鏈式）
        build(volatility_indicator, note) -> StrategyConfig:
            建構最終 StrategyConfig
    """

    def __init__(
        self,
        name: Optional[str] = None,
        base_instrument: Optional[str] = None,
        base_freq: Optional[str] = None,
        *,
        params: Optional["StrategyParams"] = None,
    ):
        """
        初始化策略建構器

        Args:
            name: 策略名稱（唯一標識）
            base_instrument: 基準商品 ID（所有指標自動繼承此值）
            base_freq: 基準頻率（支援字串，如 "15min", "1D"）
            params: StrategyParams 實例（提供時自動取 name/instrument/freq）

        Examples:
            >>> # 傳統用法
            >>> builder = StrategyBuilder(
            ...     name="KOG_US100_15T_ST_21_3",
            ...     base_instrument="US100.cash_M15_FTMO_FUTURE",
            ...     base_freq="15min"
            ... )
            >>> # 使用 params
            >>> builder = StrategyBuilder(params=p)
        """
        if params is not None:
            self.name = params.strategy_name
            self.base_instrument = params.instrument
            self.base_freq = params.base_freq
            self._params = params
        else:
            if name is None or base_instrument is None or base_freq is None:
                raise ValueError(
                    "Must provide either params or all of (name, base_instrument, base_freq)"
                )
            self.name = name
            self.base_instrument = base_instrument
            self.base_freq = base_freq
            self._params = None

        self._indicators: List["IndicatorSpec"] = []
        self._data_sources: List["DataSource"] = []
        self._base_blueprint: Optional["Blueprint"] = None
        self._advanced_blueprints: List["Blueprint"] = []

    def add_data_source(
        self,
        instrument: str,
        freq: Union["Freq", str],
    ) -> "StrategyBuilder":
        """Declare an extra (instrument, freq) data source for cross-instrument indicators.

        Args:
            instrument: Instrument key (must differ from base_instrument).
            freq: Base loading frequency for this instrument.

        Returns:
            self for chaining.

        Raises:
            ValueError: If instrument is base_instrument or already declared.
        """
        from tradepose_models.enums import Freq as FreqEnum
        from tradepose_models.strategy import DataSource

        if instrument == self.base_instrument:
            raise ValueError(
                f"Cannot add data source for base_instrument '{instrument}'. "
                "It is already loaded implicitly."
            )

        for ds in self._data_sources:
            if ds.instrument == instrument:
                raise ValueError(f"Data source for instrument '{instrument}' already declared.")

        freq_enum = FreqEnum(freq) if isinstance(freq, str) else freq
        self._data_sources.append(DataSource(instrument=instrument, freq=freq_enum))
        return self

    def add_indicator(
        self,
        indicator_type: Union["IndicatorType", str],
        freq: Union["Freq", str],
        shift: int = 1,
        instrument: Optional[str] = None,
        **kwargs,
    ) -> "IndicatorSpec":
        """
        添加指標（支援可選 instrument 覆蓋）

        Args:
            indicator_type: 指標類型（支援 IndicatorType enum 或字串）
                - IndicatorType.ATR 或 "atr"
                - IndicatorType.SMA 或 "sma"
                - IndicatorType.SUPERTREND 或 "supertrend"
            freq: 指標頻率（支援 Freq enum 或字串）
                - Freq.DAY_1 或 "1D"
                - Freq.HOUR_1 或 "1h"
                - Freq.MIN_15 或 "15min"
            shift: 位移（預設 1，依賴指標通常使用 0）
            instrument: 商品名稱（預設 None，使用 base_instrument）
                - None: 使用 self.base_instrument（常見用法）
                - "OTHER_INSTRUMENT": 使用其他商品（跨商品引用）
            **kwargs: 指標參數（傳遞給 Indicator 工廠方法）

        Returns:
            IndicatorSpec: 指標規範，提供 .col()、.display_name() 和 struct accessor

        Examples:
            >>> # 基本用法
            >>> atr = builder.add_indicator(
            ...     IndicatorType.ATR,
            ...     period=21,
            ...     freq=Freq.DAY_1,
            ...     shift=1
            ... )
            >>>
            >>> # 依賴指標（SuperTrend 引用 ATR）
            >>> st = builder.add_indicator(
            ...     IndicatorType.SUPERTREND,
            ...     multiplier=3.0,
            ...     volatility_column=atr.display_name(),  # 引用依賴
            ...     freq=Freq.DAY_1,
            ...     shift=1
            ... )
            >>>
            >>> # 使用 struct accessor
            >>> mp = builder.add_indicator(IndicatorType.MARKET_PROFILE, ...)
            >>> vah = mp.market_profile.vah  # 直接存取 struct 欄位
        """
        from tradepose_models.enums import Freq, IndicatorType
        from tradepose_models.indicators import Indicator

        from .helpers import create_indicator_spec

        # 轉換 indicator_type 為字串
        if isinstance(indicator_type, IndicatorType):
            indicator_type_str = indicator_type.value
        else:
            indicator_type_str = indicator_type

        # 動態呼叫 Indicator 工廠方法
        if not hasattr(Indicator, indicator_type_str):
            raise ValueError(
                f"Unknown indicator type: '{indicator_type_str}'. "
                f"Available: {', '.join([e.value for e in IndicatorType])}"
            )

        indicator_factory = getattr(Indicator, indicator_type_str)
        indicator_config = indicator_factory(**kwargs)

        # 轉換 freq 字串/enum 為 Freq enum
        freq_enum = Freq(freq) if isinstance(freq, str) else freq

        # 使用 instrument（如果提供），否則使用 base_instrument
        final_instrument = instrument if instrument is not None else self.base_instrument

        # Cross-instrument validation: require data_source declaration
        if final_instrument != self.base_instrument:
            ds = next(
                (ds for ds in self._data_sources if ds.instrument == final_instrument),
                None,
            )
            if ds is None:
                raise ValueError(
                    f"Instrument '{final_instrument}' requires a data source declaration. "
                    f"Call builder.add_data_source('{final_instrument}', freq) first."
                )
            # Ensure indicator freq >= data_source freq (can resample up, not down)
            if _freq_to_seconds(freq_enum) < _freq_to_seconds(ds.freq):
                raise ValueError(
                    f"Indicator freq '{freq_enum.value}' is lower than data source freq "
                    f"'{ds.freq.value}' for instrument '{final_instrument}'. "
                    f"Cannot resample down; indicator freq must be >= data source freq."
                )

        # 建立 IndicatorSpec
        spec = create_indicator_spec(
            freq=freq_enum,
            indicator=indicator_config,
            instrument=final_instrument,
            shift=shift,
        )

        self._indicators.append(spec)
        return spec

    def apply_weekly_cutoff(
        self,
        exit_expr: "pl.Expr",
        weekly_cutoff: "tuple[int, int, int] | None",
    ) -> "pl.Expr":
        """將 weekly_cutoff 強制出場條件疊加到 exit_expr。

        Args:
            exit_expr: 原始出場條件表達式
            weekly_cutoff: (weekday, hour, minute) 或 None。weekday: 0=週一 … 6=週日。

        Returns:
            若 weekly_cutoff 為 None，原樣回傳 exit_expr；
            否則回傳 exit_expr | 時間條件。

        Example:
            >>> exit_expr = self.apply_weekly_cutoff(exit_expr, self.weekly_cutoff)
        """
        import polars as pl

        if weekly_cutoff is None:
            return exit_expr
        weekday, hour, minute = weekly_cutoff
        return exit_expr | (
            (pl.col("ts").dt.weekday() == weekday)
            & (
                (pl.col("ts").dt.hour() > hour)
                | ((pl.col("ts").dt.hour() == hour) & (pl.col("ts").dt.minute() >= minute))
            )
        )

    def set_base_blueprint(self, blueprint: "Blueprint") -> "StrategyBuilder":
        """
        設定 Base Blueprint

        Args:
            blueprint: Base Blueprint 物件（通常由 BlueprintBuilder 建立）

        Returns:
            StrategyBuilder: 回傳自身，支援鏈式呼叫

        Examples:
            >>> base_bp = BlueprintBuilder("base", "Long", "Trend")\
            ...     .add_entry_trigger(...)\
            ...     .add_exit_trigger(...)\
            ...     .build()
            >>>
            >>> builder.set_base_blueprint(base_bp)
        """
        self._base_blueprint = blueprint
        return self

    def add_advanced_blueprint(self, blueprint: "Blueprint") -> "StrategyBuilder":
        """
        添加 Advanced Blueprint（鏈式呼叫）

        Args:
            blueprint: Advanced Blueprint 物件（通常由 BlueprintBuilder 建立）

        Returns:
            StrategyBuilder: 回傳自身，支援鏈式呼叫

        Examples:
            >>> adv_bp = BlueprintBuilder("risk_mgmt", "Long", "Trend")\
            ...     .add_exit_trigger(...)\
            ...     .build()
            >>>
            >>> builder.add_advanced_blueprint(adv_bp)
        """
        self._advanced_blueprints.append(blueprint)
        return self

    def build(
        self,
        volatility_indicator: Optional["pl.Expr"] = None,
        note: str = "",
    ) -> "StrategyConfig":
        """
        建構最終 StrategyConfig

        Args:
            volatility_indicator: 波動率欄位表達式（pl.Expr），如 pl.col("1D_ATR|14")
            note: 策略說明

        Returns:
            StrategyConfig: 完整的策略設定物件

        Raises:
            ValueError: 如果未設定 base_blueprint

        Examples:
            >>> strategy = builder.build(
            ...     volatility_indicator=atr.col(),
            ...     note="US100 順勢策略 - SuperTrend(21,3)"
            ... )
        """
        from tradepose_models.enums import Freq
        from tradepose_models.strategy import StrategyConfig

        if not self._base_blueprint:
            raise ValueError("Base blueprint is required. Use .set_base_blueprint() first.")

        # 轉換 base_freq 為 Freq enum
        base_freq_enum = Freq(self.base_freq) if isinstance(self.base_freq, str) else self.base_freq

        tz = self._params.tz.key if self._params is not None else "UTC"

        return StrategyConfig(
            name=self.name,
            base_instrument=self.base_instrument,
            base_freq=base_freq_enum,
            tz=tz,
            params=self._params,
            volatility_indicator=volatility_indicator,
            indicators=self._indicators,
            data_sources=self._data_sources,
            base_blueprint=self._base_blueprint,
            advanced_blueprints=self._advanced_blueprints,
            note=note,
        )

    def __repr__(self) -> str:
        """字串表示"""
        return (
            f"StrategyBuilder(name='{self.name}', "
            f"base_instrument='{self.base_instrument}', "
            f"base_freq='{self.base_freq}', "
            f"indicators={len(self._indicators)}, "
            f"advanced_blueprints={len(self._advanced_blueprints)})"
        )
