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

from __future__ import annotations

from src.trading.market_analysis.models import (
    EmaDistanceState,
    EntryTimingState,
    MarketPhase,
    MarketState,
    MarketStructure,
    MomentumState,
    TrendDirection,
    TrendQuality,
    TrendStrength,
    VolatilityState,
)


# Главный рыночный фильтр входа.
#
# Этот файл НЕ открывает сделки сам.
# Он только отвечает на вопрос:
# "Можно ли стратегии вообще рассматривать вход по текущему состоянию рынка?"
#
# Последовательность:
# 1. Проверяем, что рынок действительно трендовый.
# 2. Проверяем направление тренда.
# 3. Отсекаем плохую волатильность.
# 4. Проверяем старший таймфрейм.
# 5. Проверяем momentum / breakout.
# 6. Проверяем структуру рынка.
# 7. Проверяем EMA, свечи, цену и тайминг.
#
# ВАЖНО:
# Резкий breakout может появляться из COMPRESSED / шумного состояния.
# Поэтому COMPRESSED и NOISY теперь не всегда блокируют вход,
# если есть подтверждённый breakout по тренду.
def is_trade_allowed(
    *,
    state: MarketState,
    trend: TrendDirection,
    volatility: VolatilityState,
    trend_strength: TrendStrength,
    trend_quality: TrendQuality,
    market_phase: MarketPhase,
    market_structure: MarketStructure,
    momentum_state: MomentumState,
    momentum_direction: TrendDirection,
    candle_noise_score: float | None,
    price_position_score: float | None,
    ema_fast_slope_percent: float | None,
    ema_distance_state: EmaDistanceState,
    entry_timing_state: EntryTimingState,
    fast_slope_threshold_percent: float,
    htf_alignment: str,
    htf_confirmation_score: float | None,
    min_htf_confirmation_score: float,
    min_clean_candle_score: float,
    min_price_position_score: float,
    rsi_value: float | None,
    rsi_overbought: float,
    rsi_oversold: float,
) -> bool:
    is_up_context = (
        state == MarketState.TREND_UP
        and trend == TrendDirection.UP
    )

    is_down_context = (
        state == MarketState.TREND_DOWN
        and trend == TrendDirection.DOWN
    )

    is_breakout_up = (
        is_up_context
        and momentum_state == MomentumState.BREAKOUT_UP
        and momentum_direction == TrendDirection.UP
    )

    is_breakout_down = (
        is_down_context
        and momentum_state == MomentumState.BREAKOUT_DOWN
        and momentum_direction == TrendDirection.DOWN
    )

    is_breakout_with_trend = is_breakout_up or is_breakout_down

    if not is_up_context and not is_down_context:
        return False

    # Не торгуем только при низкой / неизвестной волатильности.
    # HIGH не блокируем полностью: для TREND это может быть нормальный импульс.
    if volatility in {
        VolatilityState.LOW,
        VolatilityState.UNKNOWN,
    }:
        return False

    if trend_strength == TrendStrength.UNKNOWN:
        return False

    if trend_strength == TrendStrength.WEAK and not is_breakout_with_trend:
        return False

    if trend_quality == TrendQuality.UNKNOWN:
        return False

    if trend_quality == TrendQuality.NOISY and not is_breakout_with_trend:
        return False

    if market_phase in {MarketPhase.RANGE, MarketPhase.SQUEEZE}:
        if not is_breakout_with_trend:
            return False

    if htf_alignment not in {"ALIGNED", "SAME_INTERVAL"}:
        return False

    if (
        htf_confirmation_score is not None
        and htf_confirmation_score < min_htf_confirmation_score
        and not is_breakout_with_trend
    ):
        return False

    if rsi_value is not None and not is_breakout_with_trend:
        if trend == TrendDirection.UP and rsi_value >= rsi_overbought:
            return False

        if trend == TrendDirection.DOWN and rsi_value <= rsi_oversold:
            return False

    if trend == TrendDirection.UP:
        if momentum_direction != TrendDirection.UP:
            return False

        if momentum_state not in {
            MomentumState.MOMENTUM_UP,
            MomentumState.BREAKOUT_UP,
        }:
            return False

        if market_structure == MarketStructure.LH_LL:
            return False

    if trend == TrendDirection.DOWN:
        if momentum_direction != TrendDirection.DOWN:
            return False

        if momentum_state not in {
            MomentumState.MOMENTUM_DOWN,
            MomentumState.BREAKOUT_DOWN,
        }:
            return False

        if market_structure == MarketStructure.HH_HL:
            return False

    fast_slope = ema_fast_slope_percent or 0.0

    if trend == TrendDirection.UP and fast_slope < fast_slope_threshold_percent:
        return False

    if trend == TrendDirection.DOWN and fast_slope > -fast_slope_threshold_percent:
        return False

    if candle_noise_score is None:
        return False

    if candle_noise_score < min_clean_candle_score and not is_breakout_with_trend:
        return False

    if price_position_score is None:
        return False

    if price_position_score < min_price_position_score and not is_breakout_with_trend:
        return False

    if ema_distance_state in {
        EmaDistanceState.OVEREXTENDED,
        EmaDistanceState.UNKNOWN,
    }:
        return False

    if ema_distance_state == EmaDistanceState.COMPRESSED and not is_breakout_with_trend:
        return False

    if entry_timing_state in {
        EntryTimingState.LATE,
        EntryTimingState.CHASING,
    }:
        return False

    if entry_timing_state == EntryTimingState.UNKNOWN and not is_breakout_with_trend:
        return False

    return True