"""소형 ZigZag + 국소 극값 하이브리드."""

from __future__ import annotations

import pandas as pd

from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal
from deepcoin.techniques.local_extrema import LocalExtremaTechnique
from deepcoin.techniques.zigzag_causal import find_causal_zigzag_signals


class MinorSwingTechnique(BaseTechnique):
    """2.5% 인과 ZigZag와 국소 극값을 결합한 하이브리드."""

    technique_id = "minor_swing"
    technique_name = "소형 스윙 하이브리드"
    category = "hybrid"
    causal = True
    description = "소형 ZigZag(2.5%) + 국소 극값 — GT 중간 눌림목 보완"

    def default_extra_params(self) -> dict:
        return {
            "reversal_pct": 2.5,
            "order": 15,
            "min_swing_pct": 2.0,
            "min_bars_between": 20,
        }

    def generate_signals(self, df: pd.DataFrame, params: TechniqueParams) -> list[TechniqueSignal]:
        reversal_pct = float(params.extra.get("reversal_pct", 2.5))
        zz_signals = find_causal_zigzag_signals(df, reversal_pct=reversal_pct)

        local_params = TechniqueParams(
            interval_min=params.interval_min,
            lookback_days=params.lookback_days,
            min_leg_pct=params.min_leg_pct,
            initial_cash_krw=params.initial_cash_krw,
            fee_rate=params.fee_rate,
            extra={
                "order": int(params.extra.get("order", 15)),
                "min_swing_pct": float(params.extra.get("min_swing_pct", 2.0)),
                "min_bars_between": int(params.extra.get("min_bars_between", 20)),
            },
        )
        local_signals = LocalExtremaTechnique().generate_signals(df, local_params)
        return _merge_signals(zz_signals + local_signals, min_bars=20)


def _merge_signals(signals: list[TechniqueSignal], min_bars: int) -> list[TechniqueSignal]:
    """방향별 근접 신호를 병합한다."""
    if not signals:
        return []

    sorted_signals = sorted(signals, key=lambda s: s.bar_index)
    merged: list[TechniqueSignal] = [sorted_signals[0]]

    for signal in sorted_signals[1:]:
        last = merged[-1]
        if signal.side == last.side and signal.bar_index - last.bar_index < min_bars:
            if signal.side == "buy" and signal.price < last.price:
                merged[-1] = signal
            elif signal.side == "sell" and signal.price > last.price:
                merged[-1] = signal
        else:
            merged.append(signal)

    return sorted(merged, key=lambda s: s.bar_index)
