"""
general_analysis 캔들·차트 변환 (Heikin-Ashi, 복수봉 패턴).
"""

from __future__ import annotations

import numpy as np
import pandas as pd

from deepcoin.analysis.general_analysis_core import ga_col


def general_analysis_apply_candles(df: pd.DataFrame) -> pd.DataFrame:
    """
    단일·복수 봉 캔들 패턴 및 Heikin-Ashi 컬럼 추가.

    Args:
        df: OHLCV.

    Returns:
        ga_* 캔들 컬럼이 추가된 DataFrame.
    """
    out = df.copy()
    o = out["Open"].astype(float)
    h = out["High"].astype(float)
    l = out["Low"].astype(float)
    c = out["Close"].astype(float)

    rng = (h - l).replace(0, np.nan)
    body = (c - o).abs()
    out[ga_col("body_ratio")] = (body / rng).fillna(0).clip(0, 1)
    upper_wick = h - np.maximum(o, c)
    lower_wick = np.minimum(o, c) - l
    out[ga_col("upper_wick_ratio")] = (upper_wick / rng).fillna(0).clip(0, 1)
    out[ga_col("lower_wick_ratio")] = (lower_wick / rng).fillna(0).clip(0, 1)
    out[ga_col("bullish")] = (c > o).astype(int)
    out[ga_col("bearish")] = (c < o).astype(int)
    out[ga_col("hammer")] = (
        (out[ga_col("lower_wick_ratio")] > 0.45) & (out[ga_col("body_ratio")] < 0.35)
    ).astype(int)
    out[ga_col("shooting_star")] = (
        (out[ga_col("upper_wick_ratio")] > 0.45) & (out[ga_col("body_ratio")] < 0.35)
    ).astype(int)
    out[ga_col("doji")] = (out[ga_col("body_ratio")] < 0.1).astype(int)

    prev_o, prev_c = o.shift(1), c.shift(1)
    out[ga_col("bullish_engulfing")] = (
        (c > o) & (prev_c < prev_o) & (c >= prev_o) & (o <= prev_c)
    ).astype(int)
    out[ga_col("bearish_engulfing")] = (
        (c < o) & (prev_c > prev_o) & (c <= prev_o) & (o >= prev_c)
    ).astype(int)

    o2, c2 = o.shift(2), c.shift(2)
    mid1 = (o.shift(1) + c.shift(1)) / 2
    out[ga_col("morning_star")] = (
        (c2 < o2)
        & (abs(c.shift(1) - o.shift(1)) < rng.shift(1) * 0.15)
        & (c > o)
        & (c > mid1)
    ).astype(int)
    out[ga_col("evening_star")] = (
        (c2 > o2)
        & (abs(c.shift(1) - o.shift(1)) < rng.shift(1) * 0.15)
        & (c < o)
        & (c < mid1)
    ).astype(int)

    out[ga_col("three_white_soldiers")] = (
        (c > o)
        & (c.shift(1) > o.shift(1))
        & (c.shift(2) > o.shift(2))
        & (c > c.shift(1))
        & (c.shift(1) > c.shift(2))
    ).astype(int)
    out[ga_col("three_black_crows")] = (
        (c < o)
        & (c.shift(1) < o.shift(1))
        & (c.shift(2) < o.shift(2))
        & (c < c.shift(1))
        & (c.shift(1) < c.shift(2))
    ).astype(int)

    # Heikin-Ashi
    ha_close = (o + h + l + c) / 4
    ha_open = ha_close.copy()
    ha_open.iloc[0] = (o.iloc[0] + c.iloc[0]) / 2
    for i in range(1, len(out)):
        ha_open.iloc[i] = (ha_open.iloc[i - 1] + ha_close.iloc[i - 1]) / 2
    out[ga_col("ha_close")] = ha_close
    out[ga_col("ha_open")] = ha_open
    out[ga_col("ha_bull")] = (ha_close > ha_open).astype(int)
    out[ga_col("ha_trend_up")] = (
        (ha_close > ha_close.shift(1)) & (ha_close.shift(1) > ha_close.shift(2))
    ).astype(int)

    return out


def general_analysis_candle_columns() -> list[str]:
    return [
        "body_ratio",
        "hammer",
        "shooting_star",
        "doji",
        "bullish_engulfing",
        "bearish_engulfing",
        "morning_star",
        "evening_star",
        "three_white_soldiers",
        "three_black_crows",
        "ha_bull",
        "ha_trend_up",
    ]
