# app/src/trading/market_analysis/phase.py 

from __future__ import annotations

from src.trading.market_analysis.models import (
    MarketPhase,
    TrendDirection,
    TrendQuality,
    TrendStrength,
    VolatilityState,
)


def classify_phase_direction(
    change_percent: float | None,
    *,
    threshold_percent: float,
) -> TrendDirection:
    if change_percent is None:
        return TrendDirection.UNKNOWN

    if change_percent >= threshold_percent:
        return TrendDirection.UP

    if change_percent <= -threshold_percent:
        return TrendDirection.DOWN

    return TrendDirection.FLAT


def phase_direction_consistency(
    *,
    closes: list[float],
    phase_direction: TrendDirection,
    phase_window: int,
) -> float | None:
    window = closes[-(phase_window + 1):]

    if len(window) < 2:
        return None

    up_moves = 0
    down_moves = 0

    for previous_price, current_price in zip(window, window[1:]):
        if current_price > previous_price:
            up_moves += 1
        elif current_price < previous_price:
            down_moves += 1

    total_moves = max(1, len(window) - 1)

    if phase_direction == TrendDirection.UP:
        return up_moves / total_moves

    if phase_direction == TrendDirection.DOWN:
        return down_moves / total_moves

    return None


def is_counter_trend_move(
    *,
    trend: TrendDirection,
    phase_direction: TrendDirection,
) -> bool:
    if trend == TrendDirection.UP:
        return phase_direction == TrendDirection.DOWN

    if trend == TrendDirection.DOWN:
        return phase_direction == TrendDirection.UP

    return False


def classify_market_phase(
    *,
    trend: TrendDirection,
    volatility: VolatilityState,
    trend_strength: TrendStrength,
    trend_quality: TrendQuality,
    rsi_value: float | None,
    phase_direction: TrendDirection,
    phase_change_percent: float | None,
    phase_direction_consistency: float | None,
    pullback_min_change_percent: float,
    pullback_min_direction_consistency: float,
) -> tuple[MarketPhase, str]:
    if volatility == VolatilityState.LOW:
        return MarketPhase.SQUEEZE, "LOW_VOLATILITY_SQUEEZE"

    if trend == TrendDirection.FLAT:
        return MarketPhase.RANGE, "FLAT_TREND_RANGE"

    if trend not in {TrendDirection.UP, TrendDirection.DOWN}:
        return MarketPhase.UNKNOWN, "UNKNOWN_TREND"

    if trend_strength == TrendStrength.WEAK:
        return MarketPhase.RANGE, "WEAK_TREND_RANGE"

    if is_counter_trend_move(
        trend=trend,
        phase_direction=phase_direction,
    ):
        if (
            phase_change_percent is not None
            and abs(phase_change_percent) >= pullback_min_change_percent
            and phase_direction_consistency is not None
            and phase_direction_consistency >= pullback_min_direction_consistency
        ):
            return MarketPhase.PULLBACK, "COUNTER_TREND_MOVE_CONFIRMED"

        return MarketPhase.RANGE, "COUNTER_TREND_MOVE_TOO_WEAK"

    if (
        trend == TrendDirection.UP
        and rsi_value is not None
        and rsi_value < 45
        and phase_direction == TrendDirection.DOWN
        and phase_change_percent is not None
        and abs(phase_change_percent) >= pullback_min_change_percent
        and phase_direction_consistency is not None
        and phase_direction_consistency >= pullback_min_direction_consistency
    ):
        return MarketPhase.PULLBACK, "UPTREND_RSI_PULLBACK_CONFIRMED_BY_PRICE"

    if (
        trend == TrendDirection.DOWN
        and rsi_value is not None
        and rsi_value > 55
        and phase_direction == TrendDirection.UP
        and phase_change_percent is not None
        and abs(phase_change_percent) >= pullback_min_change_percent
        and phase_direction_consistency is not None
        and phase_direction_consistency >= pullback_min_direction_consistency
    ):
        return MarketPhase.PULLBACK, "DOWNTREND_RSI_PULLBACK_CONFIRMED_BY_PRICE"

    return MarketPhase.IMPULSE, "WITH_TREND_OR_NEUTRAL_MOVE"