"""돈치안 채널 스윙."""

from __future__ import annotations

import pandas as pd

from deepcoin.techniques.base import BaseTechnique, TechniqueParams, TechniqueSignal


class DonchianTechnique(BaseTechnique):
    """N봉 최저가 터치 후 반등 매수, N봉 최고가 터치 후 하락 매도."""

    technique_id = "donchian"
    technique_name = "돈치안 채널"
    category = "swing"
    causal = True
    description = "돈치안(40) 채널 하단 매수·상단 매도"

    def default_extra_params(self) -> dict:
        return {"window": 40}

    def generate_signals(self, df: pd.DataFrame, params: TechniqueParams) -> list[TechniqueSignal]:
        window = int(params.extra.get("window", 40))
        low = df["low"].astype(float)
        high = df["high"].astype(float)
        close = df["close"].astype(float)

        lower_channel = low.rolling(window=window, min_periods=window).min()
        upper_channel = high.rolling(window=window, min_periods=window).max()

        signals: list[TechniqueSignal] = []
        touched_lower = False
        touched_upper = False

        for i in range(window, len(df)):
            if pd.isna(lower_channel.iloc[i]):
                continue

            low_i = float(low.iloc[i])
            high_i = float(high.iloc[i])
            close_i = float(close.iloc[i])
            lower_i = float(lower_channel.iloc[i])
            upper_i = float(upper_channel.iloc[i])
            dt = pd.Timestamp(df.iloc[i]["datetime"]).strftime("%Y-%m-%d %H:%M:%S")

            if low_i <= lower_i:
                touched_lower = True
            if touched_lower and close_i > lower_i * 1.005:
                signals.append(
                    TechniqueSignal(
                        side="buy",
                        bar_index=i,
                        price=round(close_i, 2),
                        datetime=dt,
                        confidence=0.65,
                        reason="donchian_lower_bounce",
                    )
                )
                touched_lower = False

            if high_i >= upper_i:
                touched_upper = True
            if touched_upper and close_i < upper_i * 0.995:
                signals.append(
                    TechniqueSignal(
                        side="sell",
                        bar_index=i,
                        price=round(close_i, 2),
                        datetime=dt,
                        confidence=0.65,
                        reason="donchian_upper_reject",
                    )
                )
                touched_upper = False

        return signals
