# app/src/trading/diagnostics/snapshot.py

from __future__ import annotations

import time
from typing import Any

from src.trading.auto.state import AutoTradeState
from src.core.numbers import safe_float
from src.integrations.exchange.runtime_ui import build_runtime_exchange_alerts
from src.integrations.exchange.market_data_runner import MarketDataRunner
from src.trading.execution.position_metrics import build_position_metrics
from src.trading.position.state import PositionState


class SemanticDiagnosticSnapshotBuilder:
    def build(self, state: AutoTradeState, *, is_configured: bool) -> dict[str, Any]:
        now = time.monotonic()

        signal_age_seconds = self._age_seconds(
            now=now,
            started_at=state.signal_started_at,
        )

        market_age_seconds = self._age_seconds(
            now=now,
            started_at=state.market_analysis_updated_at,
        )

        blockers = self._blockers(state)
        health_score = self._health_score(state=state, blockers=blockers)
        severity = self._severity(
            state=state,
            health_score=health_score,
            blockers=blockers,
        )

        position_current_price = self._position_current_price(state)

        position_health = self._position_health(
            state=state,
            current_price=position_current_price,
        )

        runtime_exchange_alerts = self._runtime_exchange_alerts(state)

        return {
            "status": {
                "status": state.status,
                "symbol": state.symbol,
                "strategy": state.strategy,
                "is_configured": is_configured,
            },
            "signal": {
                "signal": state.last_signal,
                "confidence": state.last_signal_confidence,
                "decision_status": state.decision_status,
                "is_confirmed": state.is_signal_confirmed,
                "is_ready": state.is_signal_ready,
                "repeat_count": state.last_signal_repeat_count,
                "required_repeats": state.signal_confirmation_missing_repeats
                + state.last_signal_repeat_count,
                "confirmation_progress": state.signal_confirmation_progress,
                "age_seconds": signal_age_seconds,
                "reason": state.last_signal_reason,
            },
            "market": {
                "state": state.market_state,
                "trend": state.market_trend,
                "volatility": state.market_volatility,
                "trend_strength": state.market_trend_strength,
                "trend_quality": state.market_trend_quality,
                "phase": state.market_phase,
                "phase_direction": state.market_phase_direction,
                # Таймфрейм локального анализа рынка.
                "interval": state.market_analysis_interval,
                "current_interval_change_percent": state.current_interval_change_percent,
                "current_interval_direction": state.current_interval_direction,
                "current_interval_label": state.current_interval_label,
                "market_score": state.market_score,
                "market_score_label": state.market_score_label,
                "market_long_score": state.market_long_score,
                "market_short_score": state.market_short_score,
                "last_closed_candle_change_percent": state.last_closed_candle_change_percent,
                "last_closed_candle_direction": state.last_closed_candle_direction,
                "entry_block_reason": state.entry_block_reason,
                "entry_block_message": state.entry_block_message,
                "age_seconds": market_age_seconds,
                "market_is_open": state.market_is_open,
                "market_status": state.market_status,
                "market_status_message": state.market_status_message,
                "market_status_updated_at": state.market_status_updated_at,
                "market_structure": state.market_structure,
                "market_structure_reason": state.market_structure_reason,
                "trend_gap_percent": state.market_trend_gap_percent,
                "trend_consistency": state.market_trend_consistency,
                "trend_efficiency": state.market_trend_efficiency,
                "trend_quality_score": state.trend_quality_score,
                "ema_distance_atr_ratio": state.ema_distance_atr_ratio,
                "ema_distance_state": state.ema_distance_state,
                "entry_timing_state": state.entry_timing_state,
                "entry_timing_reason": state.entry_timing_reason,
                "ema_fast_slope_percent": state.ema_fast_slope_percent,
                "ema_slow_slope_percent": state.ema_slow_slope_percent,
                "candle_noise_score": state.candle_noise_score,
                "price_position_score": state.price_position_score,
                "htf_interval": state.htf_interval,
                "htf_atr_percent": state.htf_atr_percent,
                "htf_atr_percent_baseline": state.htf_atr_percent_baseline,
                "htf_volatility_ratio": state.htf_volatility_ratio,
                "htf_volatility": state.htf_volatility,
                "htf_market_state": state.htf_market_state,
                "htf_trend": state.htf_trend,
                "htf_trend_strength": state.htf_trend_strength,
                "htf_trend_quality": state.htf_trend_quality,
                "htf_market_phase": state.htf_market_phase,
                "htf_alignment": state.htf_alignment,
                "htf_confirmation_score": state.htf_confirmation_score,
                "htf_reason": state.htf_reason,
            },
            "momentum": {
                "state": getattr(state, "momentum_state", None),
                "direction": getattr(state, "momentum_direction", None),
                "strength": getattr(state, "momentum_strength", None),
                "change_percent": getattr(state, "momentum_change_percent", None),
                "breakout_level": getattr(state, "breakout_level", None),
                "breakout_distance_percent": getattr(
                    state,
                    "breakout_distance_percent",
                    None,
                ),
                "is_breakout": getattr(state, "momentum_state", None)
                in {"BREAKOUT_UP", "BREAKOUT_DOWN"},
                "breakout_reason": getattr(state, "breakout_reason", None),
            },
            "execution": {
                "quality": state.execution_quality,
                "quality_reason": state.execution_quality_reason,
                "quality_message": state.execution_quality_message,
                "semantic_status": state.execution_semantic_status,
                "semantic_message": state.execution_semantic_message,
                "semantic_reason": state.execution_semantic_reason,
                "confidence_score": state.execution_confidence_score,
                "confidence_level": state.execution_confidence_level,
                "confidence_reason": state.execution_confidence_reason,
                "spread_percent": state.spread_percent,
                "snapshot_age_seconds": state.snapshot_age_seconds,
                "market_runtime_degraded": state.market_runtime_degraded,
            },
            "adaptive_size": {
                "base": state.adaptive_size_base,
                "final": state.adaptive_size_final,
                "multiplier": state.adaptive_size_multiplier,
                "effective_risk_percent": state.effective_risk_percent,
                "effective_target_risk_usd": state.effective_target_risk_usd,
                "reason": state.adaptive_size_reason,
                "factors": state.adaptive_size_factors,
                # Общая оценка рынка на момент расчёта размера позиции.
                "market_score": state.market_score,
                "market_score_label": state.market_score_label,
                "market_long_score": state.market_long_score,
                "market_short_score": state.market_short_score,
            },
            "position": {
                "side": state.position_side,
                "entry_price": state.entry_price,
                "size": state.position_size,
                "leverage": state.leverage,
                "unrealized_pnl_usd": state.unrealized_pnl_usd,
                "realized_pnl_usd": state.realized_pnl_usd,
                "cycle_realized_pnl_usd": state.cycle_realized_pnl_usd,
                "last_execution_action": state.last_execution_action,
                "last_execution_reason": state.last_execution_reason,
                "last_flip_old_side": state.last_flip_old_side,
                "last_flip_new_side": state.last_flip_new_side,
                "last_flip_pnl_usd": state.last_flip_pnl_usd,
                "last_flip_reason": state.last_flip_reason,
                "last_flip_monotonic_at": state.last_flip_monotonic_at,
                "current_price": position_current_price,
                "adaptive_size_multiplier": state.adaptive_size_multiplier,
                "stop_loss_usd": state.effective_target_risk_usd,
                "take_profit_usd": self._take_profit_usd(state),
                "max_loss_usd": state.max_loss_usd,
                "opened_age_seconds": self._age_seconds(
                    now=now,
                    started_at=state.position_opened_monotonic_at,
                ),
                "price_move_percent": position_health.get("price_move_percent"),
                "risk_used_percent": position_health.get("risk_used_percent"),
                "health_state": position_health.get("health_state"),
                "health_score": position_health.get("health_score"),
                "health_message": position_health.get("health_message"),
                "pressure_state": position_health.get("pressure_state"),
                "trend_alignment": position_health.get("trend_alignment"),
                "adverse_momentum": position_health.get("adverse_momentum"),
            },
            "runtime_health": {
                "market_data_runtime": MarketDataRunner.get_runtime_state("auto"),
                "exchange_statuses": runtime_exchange_alerts,
                "exchange_status": (
                    runtime_exchange_alerts[0]
                    if runtime_exchange_alerts
                    else None
                ),
                "health_score": health_score,
                "severity": severity,
                "is_runtime_degraded": self._is_runtime_degraded(state),
                "signal_age_seconds": signal_age_seconds,
                "market_age_seconds": market_age_seconds,
                "runtime_expired_reason": state.runtime_expired_reason,
                "runtime_expired_message": state.runtime_expired_message,
                "has_market_data": state.market_state is not None,
                "has_momentum_data": getattr(state, "momentum_state", None) is not None,
                "position_health_state": position_health.get("health_state"),
                "position_health_score": position_health.get("health_score"),
            },
            "summary": {
                "health_score": health_score,
                "severity": severity,
                "assessment": self._assessment(severity),
                "mode": self._display_mode(
                    severity=severity,
                    blockers=blockers,
                    state=state,
                ),

                "headline_mode": (
                    "POSITION"
                    if state.position_side != "NONE"
                    else "ENTRY"
                ),

                "main_message": self._main_message(state=state, blockers=blockers),

                "market": state.market_state,
                "market_score": state.market_score,
                "market_score_label": state.market_score_label,
                "market_long_score": state.market_long_score,
                "market_short_score": state.market_short_score,
                "phase": state.market_phase,
                "momentum": getattr(state, "momentum_state", None),
                "execution": state.execution_semantic_status,
                "position": state.position_side,
                "position_health": position_health.get("health_state"),
                "is_ready": state.is_signal_ready,
                "is_blocked": bool(blockers),
                "blockers": blockers,
                "symbol": state.symbol,
            },
        }

    def _position_current_price(
        self,
        state: AutoTradeState,
    ) -> float | None:
        if state.position_side == "NONE":
            return None

        try:
            from src.integrations.exchange.service import ExchangeService

            quote = ExchangeService().get_quote(
                state.symbol,
                runtime_key="auto",
            )

            side = str(state.position_side or "").upper()

            price = quote.last_price

            if side == "LONG":
                price = quote.bid_price or price

            elif side == "SHORT":
                price = quote.ask_price or price

            return safe_float(price)

        except Exception:
            return None
    
    def _age_seconds(
        self,
        *,
        now: float,
        started_at: float | None,
    ) -> int | None:
        started = safe_float(started_at)

        if started is None:
            return None

        return max(0, int(now - started))

    def _is_runtime_degraded(self, state: AutoTradeState) -> bool:
        return bool(
            state.market_runtime_degraded
            or state.execution_quality == "BLOCKED"
            or state.runtime_expired_reason
        )

    def _health_score(
        self,
        *,
        state: AutoTradeState,
        blockers: list[str],
    ) -> int:
        score = 100

        # Если MarketAnalysisService уже дал общую оценку рынка,
        # diagnostics-health лучше строить от неё, а дальше корректировать
        # runtime-блокировками, execution quality и статусом автоторговли.
        market_score = safe_float(getattr(state, "market_score", None))
        if market_score is not None:
            score = int(max(0, min(100, market_score)))

        if state.status == "OFF":
            score -= 25
        elif state.status != "RUNNING":
            score -= 10

        if blockers:
            score -= min(35, len(blockers) * 12)

        if state.execution_quality == "BLOCKED":
            score -= 30
        elif state.execution_quality == "WARNING":
            score -= 15

        if market_score is None:
            # Старый fallback: если общей оценки рынка нет,
            # health_score собирается из отдельных market-метрик.
            if state.market_state in {"RANGE", "HIGH_VOLATILITY", "LOW_VOLATILITY"}:
                score -= 15

            if state.market_trend_strength == "WEAK":
                score -= 10

            if state.market_trend_quality == "NOISY":
                score -= 10

            if state.market_phase in {"RANGE", "SQUEEZE", "PULLBACK"}:
                score -= 10

            if state.market_structure == "MIXED":
                score -= 15

            if state.market_structure == "HH_HL" and state.market_trend == "DOWN":
                score -= 20

            if state.market_structure == "LH_LL" and state.market_trend == "UP":
                score -= 20

            if state.ema_distance_state == "COMPRESSED":
                score -= 10

            if state.ema_distance_state == "EXTENDED":
                score -= 8

            if state.ema_distance_state == "OVEREXTENDED":
                score -= 25

            if state.entry_timing_state == "LATE":
                score -= 18

            if state.entry_timing_state == "CHASING":
                score -= 30

            trend_quality_score = safe_float(state.trend_quality_score)
            if trend_quality_score is not None:
                if trend_quality_score < 0.45:
                    score -= 12
                elif trend_quality_score >= 0.7:
                    score += 5

        if state.market_runtime_degraded:
            score -= 15

        if state.runtime_expired_reason:
            score -= 20

        if state.is_signal_ready:
            score += 10

        return max(0, min(100, score))

    def _severity(
        self,
        *,
        state: AutoTradeState,
        health_score: int,
        blockers: list[str],
    ) -> str:
        signal = str(state.last_signal or "HOLD").upper()
        has_ready_signal = bool(state.is_signal_ready)
        has_position = state.position_side != "NONE"

        if state.market_is_open is False:
            return "RED"
        
        if state.market_score is not None and state.market_score < 25:
            return "RED"

        has_waiting_data_blocker = any(
            str(item).strip().lower()
            in {
                "мало данных",
                "мало live-данных",
                "недостаточно live-данных",
            }
            for item in blockers
        )

        if has_waiting_data_blocker:
            return "WAITING"

        # Структура рынка — это hard-block, а не обычное ожидание.
        if state.entry_block_reason in {
            "MARKET_STRUCTURE_CONFLICT",
            "MARKET_STRUCTURE_MIXED",
        }:
            return "RED"

        # Market filter сначала оцениваем как блокировку,
        # иначе HOLD преждевременно вернёт WAITING.
        if state.entry_block_reason == "MARKET_FILTER_BLOCKED":
            if state.market_phase in {"PULLBACK", "RANGE", "SQUEEZE"}:
                return "RED"

            if state.market_trend_quality == "NOISY":
                return "RED"

            return "YELLOW"

        if (
            state.execution_quality == "BLOCKED"
            or state.decision_status == "BLOCKED"
            or state.runtime_expired_reason
        ):
            return "RED"

        if has_position:
            if health_score < 45:
                return "RED"

            if blockers or state.execution_quality == "WARNING" or health_score < 75:
                return "YELLOW"

            return "GREEN"

        if signal == "HOLD" and not has_ready_signal:
            return "WAITING"

        if health_score < 45:
            return "YELLOW"

        if blockers or state.execution_quality == "WARNING" or health_score < 75:
            return "YELLOW"

        return "GREEN"

    def _assessment(self, severity: str) -> str:
        if severity == "GREEN":
            return "стабильно"

        if severity == "WAITING":
            return "ожидание"

        if severity == "YELLOW":
            return "осторожно"

        return "вход нежелателен"

    def _display_mode(
        self,
        *,
        severity: str,
        blockers: list[str],
        state: AutoTradeState | None = None,
    ) -> str:
        if state is not None and state.position_side != "NONE":
            return "EXPANDED"

        if severity == "GREEN" and not blockers:
            return "COMPACT"

        return "EXPANDED"

    def _main_message(
        self,
        *,
        state: AutoTradeState,
        blockers: list[str],
    ) -> str:
        if state.market_is_open is False:
            return state.market_status_message or "Биржа временно недоступна для торговли."

        # Структура рынка — отдельная жёсткая причина блокировки,
        # чтобы UI не показывал её как обычное ожидание рынка.
        if state.entry_block_reason == "MARKET_STRUCTURE_CONFLICT":
            return "Вход заблокирован: структура рынка против направления."

        if state.entry_block_reason == "MARKET_STRUCTURE_MIXED":
            return "Вход заблокирован: структура рынка не подтверждает направление."

        if state.entry_block_reason == "MARKET_FILTER_BLOCKED":
            return self._market_filter_message(state)

        if state.execution_quality == "BLOCKED":
            reason = str(state.execution_quality_reason or "")

            if reason == "HIGH_SPREAD":
                return "Вход нежелателен: спред мешает входу."

            if reason == "STALE_SNAPSHOT":
                return "Вход нежелателен: данные рынка устарели."

            if reason in {"SNAPSHOT_ERROR", "SNAPSHOT_UNAVAILABLE"}:
                return "Вход нежелателен: нет надёжных данных рынка."

            return "Вход нежелателен: исполнение заблокировано."

        if state.entry_block_message:
            return f"Рынок не готов: {state.entry_block_message}."

        if state.execution_quality == "WARNING":
            return "Вход рискованный: качество исполнения снижено."

        if state.is_signal_ready:
            return "Сигнал готов, вход разрешён."

        if state.last_signal in {"BUY", "SELL"}:
            return "Сигнал есть, идёт подтверждение."

        if blockers:
            return f"Есть ограничения: {', '.join(blockers)}."

        return "Критичных ограничений нет."

    def _market_filter_message(self, state: AutoTradeState) -> str:
        if state.market_volatility == "HIGH":
            return "Ожидание: движение слишком резкое, вход сейчас рискованный."

        if state.entry_timing_state in {"LATE", "CHASING"}:
            return "Ожидание: цена уже сильно прошла, входить поздно."

        if state.ema_distance_state == "OVEREXTENDED":
            return "Ожидание: цена ушла слишком далеко после импульса."

        if state.ema_distance_state == "COMPRESSED":
            return "Ожидание: рынок слишком сжат, направление ещё не подтвердилось."

        if state.market_trend_quality == "NOISY":
            return "Ожидание: движение есть, но оно шумное и ненадёжное."

        if state.market_structure == "MIXED":
            return "Ожидание: структура рынка противоречивая."

        if state.market_phase == "PULLBACK":
            return "Ожидание: рынок в откате, ждём подтверждения продолжения."

        if state.market_state == "RANGE" or state.market_phase in {"RANGE", "SQUEEZE"}:
            return "Ожидание: рынок пока без понятного направления."

        if state.entry_block_message:
            return f"Ожидание: {state.entry_block_message}."

        return "Ожидание: условия для входа пока не совпали."

    def _blockers(self, state: AutoTradeState) -> list[str]:
        blockers: list[str] = []

        if state.market_is_open is False:
            blockers.append(
                state.market_status_message
                or "рынок закрыт"
            )
            return blockers

        if state.entry_block_reason in {
            "MARKET_STRUCTURE_CONFLICT",
            "MARKET_STRUCTURE_MIXED",
        }:
            blockers.append(str(state.entry_block_message or "структура рынка не подтверждает вход"))
            
        if state.entry_block_reason == "MARKET_FILTER_BLOCKED":
            if state.market_state == "RANGE" or state.market_phase == "RANGE":
                blockers.append(self._market_filter_message(state).replace("Ожидание: ", "").rstrip("."))
            elif state.entry_block_message:
                blockers.append(str(state.entry_block_message))
            else:
                blockers.append("рынок не подходит")

        if state.ema_distance_state == "COMPRESSED":
            blockers.append("EMA слишком сжаты")

        if state.ema_distance_state == "OVEREXTENDED":
            blockers.append("тренд перерастянут")

        if state.entry_timing_state == "LATE":
            blockers.append("поздний вход")

        if state.entry_timing_state == "CHASING":
            blockers.append("вход запрещён: chasing move")

        # Добавляем entry_block_message только если это не тот же текст,
        # который уже был добавлен выше через MARKET_FILTER_BLOCKED / STRUCTURE.
        if state.entry_block_message:
            message = str(state.entry_block_message)

            normalized = message.strip()

            if normalized and normalized not in blockers:
                blockers.append(normalized)

        if state.execution_quality == "BLOCKED":
            blockers.append(str(state.execution_quality_message or "исполнение заблокировано"))

        if state.decision_status == "BLOCKED":
            blockers.append(str(state.decision_reason or "решение заблокировано"))

        if state.runtime_expired_message:
            blockers.append(str(state.runtime_expired_message))

        result: list[str] = []
        for item in blockers:
            if item and item not in result:
                result.append(item)

        return result
    
    def _position_health(
        self,
        *,
        state: AutoTradeState,
        current_price: float | None,
    ) -> dict[str, Any]:
        if state.position_side == "NONE":
            return {
                "health_state": "NONE",
                "health_score": None,
                "health_message": None,
                "price_move_percent": None,
                "risk_used_percent": None,
                "pressure_state": "NONE",
                "trend_alignment": "NONE",
                "adverse_momentum": False,
            }

        entry_price = safe_float(state.entry_price)
        position_size = safe_float(state.position_size)
        pnl = safe_float(state.unrealized_pnl_usd)
        stop_loss_usd = safe_float(state.effective_target_risk_usd)
        max_loss_usd = safe_float(state.max_loss_usd)

        metrics = build_position_metrics(
            PositionState(
                side=state.position_side or "NONE",
                symbol=state.symbol,
                entry_price=entry_price,
                size=position_size,
                leverage=state.leverage,
                unrealized_pnl_usd=pnl,
                opened_monotonic_at=state.position_opened_monotonic_at,
            ),
            current_price=current_price,
        )

        price_move_percent = metrics.price_move_percent

        risk_used_percent = self._position_risk_used_percent(
            pnl=pnl,
            stop_loss_usd=stop_loss_usd,
            max_loss_usd=max_loss_usd,
        )

        health_state = str(state.position_health_status or "")
        health_score = state.position_health_score
        health_message = state.position_health_reason
        pressure_state = str(state.position_pressure or "")
        trend_alignment = str(state.position_trend_alignment or "")
        adverse_momentum = bool(state.position_adverse_momentum)

        if not trend_alignment:
            trend_alignment = self._position_trend_alignment(state)

        if not adverse_momentum:
            adverse_momentum = self._has_adverse_momentum(state)

        if not pressure_state:
            pressure_state = self._position_pressure_state(
                pnl=pnl,
                risk_used_percent=risk_used_percent,
                adverse_momentum=adverse_momentum,
            )

        if not health_state:
            health_score = self._position_health_score(
                pnl=pnl,
                risk_used_percent=risk_used_percent,
                trend_alignment=trend_alignment or "NEUTRAL",
                adverse_momentum=adverse_momentum,
                pressure_state=pressure_state or "UNKNOWN",
                opened_age_seconds=self._age_seconds(
                    now=time.monotonic(),
                    started_at=state.position_opened_monotonic_at,
                ),
            )

            health_state = self._position_health_state(health_score)

        if not health_message:
            health_message = self._position_health_message(
                health_state=health_state,
                pressure_state=pressure_state or "UNKNOWN",
                trend_alignment=trend_alignment or "NEUTRAL",
                adverse_momentum=adverse_momentum,
            )

        return {
            "health_state": health_state,
            "health_score": health_score,
            "health_message": health_message,
            "price_move_percent": price_move_percent,
            "risk_used_percent": risk_used_percent,
            "pressure_state": pressure_state,
            "trend_alignment": trend_alignment,
            "adverse_momentum": adverse_momentum,
        }

    def _position_risk_used_percent(
        self,
        *,
        pnl: float | None,
        stop_loss_usd: float | None,
        max_loss_usd: float | None,
    ) -> float | None:
        if pnl is None or pnl >= 0:
            return 0.0

        candidates = [
            value
            for value in [stop_loss_usd, max_loss_usd]
            if value is not None and value > 0
        ]

        if not candidates:
            return None

        risk_base = min(candidates)

        if risk_base is None or risk_base <= 0:
            return None

        return round(min(999.0, (abs(pnl) / abs(risk_base)) * 100), 1)

    def _position_trend_alignment(self, state: AutoTradeState) -> str:
        side = str(state.position_side or "").upper()
        market_state = str(state.market_state or "").upper()
        trend = str(state.market_trend or "").upper()

        if side == "LONG":
            if market_state == "TREND_UP" or trend == "UP":
                return "ALIGNED"

            if market_state == "TREND_DOWN" or trend == "DOWN":
                return "AGAINST"

        if side == "SHORT":
            if market_state == "TREND_DOWN" or trend == "DOWN":
                return "ALIGNED"

            if market_state == "TREND_UP" or trend == "UP":
                return "AGAINST"

        return "NEUTRAL"

    def _has_adverse_momentum(self, state: AutoTradeState) -> bool:
        side = str(state.position_side or "").upper()
        momentum_direction = str(state.momentum_direction or "").upper()
        momentum_state = str(state.momentum_state or "").upper()

        if side == "LONG":
            return (
                momentum_direction == "DOWN"
                or momentum_state in {"MOMENTUM_DOWN", "BREAKOUT_DOWN"}
            )

        if side == "SHORT":
            return (
                momentum_direction == "UP"
                or momentum_state in {"MOMENTUM_UP", "BREAKOUT_UP"}
            )

        return False

    def _position_pressure_state(
        self,
        *,
        pnl: float | None,
        risk_used_percent: float | None,
        adverse_momentum: bool,
    ) -> str:
        if pnl is None:
            return "UNKNOWN"

        if pnl >= 0:
            if adverse_momentum:
                return "PROFIT_UNDER_PRESSURE"

            return "PROFIT"

        if risk_used_percent is None:
            return "LOSS"

        if risk_used_percent >= 80:
            return "DANGER"

        if risk_used_percent >= 50:
            return "PRESSURE"

        return "LOSS"

    def _position_health_score(
        self,
        *,
        pnl: float | None,
        risk_used_percent: float | None,
        trend_alignment: str,
        adverse_momentum: bool,
        pressure_state: str,
        opened_age_seconds: int | None,
    ) -> int:
        score = 100

        if pnl is not None:
            if pnl < 0:
                score -= 20
            elif pnl > 0:
                score += 5

        if risk_used_percent is not None:
            if risk_used_percent >= 80:
                score -= 45
            elif risk_used_percent >= 50:
                score -= 30
            elif risk_used_percent >= 25:
                score -= 15

        if trend_alignment == "AGAINST":
            score -= 25
        elif trend_alignment == "ALIGNED":
            score += 8

        if adverse_momentum:
            score -= 20

        if pressure_state == "DANGER":
            score -= 20
        elif pressure_state == "PRESSURE":
            score -= 10

        if opened_age_seconds is not None:
            if opened_age_seconds >= 7200:
                score -= 20
            elif opened_age_seconds >= 3600:
                score -= 10
                
        return max(0, min(100, score))

    def _position_health_state(self, score: int | None) -> str:
        if score is None:
            return "UNKNOWN"

        if score >= 75:
            return "HEALTHY"

        if score >= 50:
            return "WATCH"

        if score >= 30:
            return "PRESSURE"

        return "DANGER"

    def _position_health_message(
        self,
        *,
        health_state: str,
        pressure_state: str,
        trend_alignment: str,
        adverse_momentum: bool,
    ) -> str:
        if health_state == "HEALTHY":
            return "Позиция выглядит устойчиво."

        if health_state == "WATCH":
            return "Позиция требует наблюдения."

        if health_state == "PRESSURE":
            if trend_alignment == "AGAINST":
                return "Позиция под давлением: рынок против направления."

            if adverse_momentum:
                return "Позиция под давлением: импульс против позиции."

            return "Позиция под давлением."

        if health_state == "DANGER":
            return "Высокий риск по открытой позиции."

        return "Состояние позиции не определено."
    
    def _take_profit_usd(self, state: AutoTradeState) -> float | None:
        take_profit_percent = safe_float(state.take_profit_percent)
        position_size = safe_float(state.position_size)
        entry_price = safe_float(state.entry_price)

        if (
            take_profit_percent is None
            or position_size is None
            or position_size <= 0
            or entry_price is None
            or entry_price <= 0
        ):
            return None

        move = entry_price * (take_profit_percent / 100)
        return move * position_size
    
    def _runtime_exchange_alerts(
        self,
        state: AutoTradeState,
    ) -> list[dict[str, Any]]:
        return build_runtime_exchange_alerts(symbol=state.symbol)