"""인과 ZigZag — 피벗 확정 시점에 매수·매도 신호."""

from __future__ import annotations

import pandas as pd

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


class ZigzagCausalTechnique(BaseTechnique):
    """되돌림 % 확정 시 피벗을 인정하는 인과 ZigZag 기법.

    Ground Truth ZigZag와 동일 임계값을 쓰되, 미래 데이터 없이
    피벗이 확정된 봉에서만 신호를 발생시킨다.
    """

    technique_id = "zigzag_causal"
    technique_name = "인과 ZigZag"
    category = "swing"
    causal = True
    description = "되돌림 % 확정 시 스윙 저점 매수·고점 매도 (GT ZigZag 인과 버전)"

    def default_extra_params(self) -> dict:
        return {"reversal_pct": 5.0}

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


def find_causal_zigzag_signals(
    df: pd.DataFrame,
    reversal_pct: float = 5.0,
) -> list[TechniqueSignal]:
    """인과 ZigZag 신호를 생성한다.

    Args:
        df: OHLCV DataFrame.
        reversal_pct: 피벗 확정 최소 되돌림(%).

    Returns:
        시간순 신호 리스트.
    """
    if len(df) < 3:
        return []

    threshold = reversal_pct / 100.0
    signals: list[TechniqueSignal] = []
    direction: str | None = None
    extreme_idx = 0
    extreme_price = float(df.iloc[0]["close"])

    for i in range(1, len(df)):
        high = float(df.iloc[i]["high"])
        low = float(df.iloc[i]["low"])

        if direction is None:
            if high >= extreme_price * (1 + threshold):
                direction = "up"
                extreme_idx = i
                extreme_price = high
            elif low <= extreme_price * (1 - threshold):
                direction = "down"
                extreme_idx = i
                extreme_price = low
            continue

        if direction == "up":
            if high >= extreme_price:
                extreme_price = high
                extreme_idx = i
            if low <= extreme_price * (1 - threshold):
                signals.append(
                    _make_signal(df, i, extreme_idx, extreme_price, "sell", reversal_pct)
                )
                direction = "down"
                extreme_idx = i
                extreme_price = low
        else:
            if low <= extreme_price:
                extreme_price = low
                extreme_idx = i
            if high >= extreme_price * (1 + threshold):
                signals.append(
                    _make_signal(df, i, extreme_idx, extreme_price, "buy", reversal_pct)
                )
                direction = "up"
                extreme_idx = i
                extreme_price = high

    return signals


def _make_signal(
    df: pd.DataFrame,
    confirm_idx: int,
    pivot_idx: int,
    pivot_price: float,
    side: str,
    reversal_pct: float,
) -> TechniqueSignal:
    """확정 봉 기준 TechniqueSignal을 생성한다."""
    dt = pd.Timestamp(df.iloc[confirm_idx]["datetime"]).strftime("%Y-%m-%d %H:%M:%S")
    return TechniqueSignal(
        side=side,
        bar_index=confirm_idx,
        price=round(pivot_price, 2),
        datetime=dt,
        pivot_bar_index=pivot_idx,
        confidence=min(1.0, reversal_pct / 10.0),
        reason=f"zigzag_{side}_confirmed",
    )
