# app/src/trading/execution/engine.py

from __future__ import annotations

from src.trading.auto.state import AutoTradeState
from src.trading.execution.models import ExecutionDecision
from src.trading.position.state import PositionState
from src.trading.execution.pricing import ExecutionPricingMixin
from src.trading.execution.position_runtime import ExecutionPositionRuntimeMixin
from src.trading.execution.position_exit_decision import ExecutionPositionExitDecisionMixin
from src.trading.execution.position_protection import ExecutionPositionProtectionMixin
from src.trading.execution.supervisor import ExecutionSupervisorMixin
from src.trading.execution.sizing import ExecutionSizingMixin
from src.trading.execution.risk_close import ExecutionRiskCloseMixin
from src.trading.execution.flip import ExecutionFlipMixin
from src.trading.execution.position_actions import ExecutionPositionActionsMixin
from src.trading.execution.runtime_actions import ExecutionRuntimeActionsMixin
from src.trading.execution.calculations import ExecutionCalculationsMixin
from src.trading.execution.resets import ExecutionResetsMixin
from src.trading.execution.constants import (
    EXECUTION_ACTION_NONE,
    EXECUTION_ACTION_OPEN_LONG,
    EXECUTION_ACTION_OPEN_SHORT,
    EXECUTION_DECISION_READY,
    EXECUTION_STATUS_RUNNING,
    POSITION_SIDE_LONG,
    POSITION_SIDE_SHORT,
    SIGNAL_BUY,
    SIGNAL_SELL,
)


class ExecutionEngine(
    ExecutionCalculationsMixin,
    ExecutionResetsMixin,
    ExecutionPricingMixin,
    ExecutionPositionRuntimeMixin,
    ExecutionPositionExitDecisionMixin,
    ExecutionSizingMixin,
    ExecutionPositionActionsMixin,
    ExecutionPositionProtectionMixin,
    ExecutionSupervisorMixin,
    ExecutionRiskCloseMixin,
    ExecutionFlipMixin,
    ExecutionRuntimeActionsMixin,
):
    _position = PositionState()
    _size_precision = 5
    _min_flip_confidence = 0.65
    _min_flip_repeat_count = 2
    _min_flip_hold_seconds = 20
    _flip_cooldown_seconds = 45
    _loss_flip_confidence = 0.75
    _last_flip_block_key: str | None = None
    _last_flip_diagnostic_key: str | None = None
    _runtime_action_cooldown_seconds = 30
    _last_runtime_action_key: str | None = None
    _emergency_halt_drawdown_usd = 250.0
    _emergency_halt_loss_streak = 5

    _execution_cooldown_after_loss_seconds = 90

    _max_execution_snapshot_age_seconds = 5

    _degraded_market_block_states = {
        "CHAOTIC",
        "LIQUIDITY_VOID",
    }

    _conflict_execution_block = True

    _last_supervisor_block_key: str | None = None

    # вернуть ExecutionDecision без выполнения торгового действия
    def _skip_execution(
        self,
        state: AutoTradeState,
        reason: str,
    ) -> ExecutionDecision:
        state.last_execution_action = EXECUTION_ACTION_NONE
        state.last_execution_reason = reason

        return ExecutionDecision(
            EXECUTION_ACTION_NONE,
            False,
            reason,
        )

    def process(self, state: AutoTradeState) -> ExecutionDecision:
        # Synchronize runtime state
        self._sync_state_from_position(state)

        if state.status != EXECUTION_STATUS_RUNNING:
            return self._skip_execution(
                state,
                "Execution доступен только в режиме RUNNING.",
            )

        self._update_unrealized_pnl(state)

        # Emergency risk management
        risk_decision = self._risk_close_decision(state)
        if risk_decision is not None:
            return risk_decision

        # Runtime position protection
        protection_decision = self._process_runtime_protection(state)
        if protection_decision is not None:
            return protection_decision

        # Flip diagnostics before READY / supervisor.
        # Это не меняет торговую логику: только фиксирует, что противоположный
        # сигнал появился при уже открытой позиции.
        flip_requested = self._should_flip_position(state)

        if flip_requested:
            self._log_flip_diagnostic(
                state=state,
                stage="FLIP_REQUESTED",
                reason="opposite signal while position is open",
            )

        # Signal readiness validation
        if state.decision_status != EXECUTION_DECISION_READY or not state.is_signal_ready:
            reason = (
                f"Execution ожидает READY "
                f"(decision={state.decision_status}, "
                f"ready={state.is_signal_ready})."
            )

            if flip_requested:
                self._log_flip_diagnostic(
                    state=state,
                    stage="FLIP_BLOCKED_BY_READY",
                    reason=reason,
                )

            return self._skip_execution(
                state,
                reason,
            )

        # Execution supervisor
        supervisor_decision = self._process_execution_supervisor(state)
        if supervisor_decision is not None:
            if flip_requested:
                self._log_flip_diagnostic(
                    state=state,
                    stage="FLIP_BLOCKED_BY_SUPERVISOR",
                    reason=supervisor_decision.reason,
                )

            return supervisor_decision

        # Existing position validation
        position = type(self)._position

        # Не пытаемся повторно открыть позицию в ту же сторону.
        # Сигнал остаётся валидным для UI/Telegram, но execution не дублируется.
        if position.side == POSITION_SIDE_LONG and state.last_signal == SIGNAL_BUY:
            return self._skip_execution(
                state,
                "Сигнал BUY совпадает с уже открытой LONG позицией.",
            )

        if position.side == POSITION_SIDE_SHORT and state.last_signal == SIGNAL_SELL:
            return self._skip_execution(
                state,
                "Сигнал SELL совпадает с уже открытой SHORT позицией.",
            )

        # Position flip
        if flip_requested:
            flip_block_reason = self._flip_block_reason(state)

            if flip_block_reason is not None:
                return self._block_flip(state, flip_block_reason)

            return self._flip_position(state)

        # New position opening
        if state.last_signal == SIGNAL_BUY:
            return self._open_position_if_empty(
                state=state,
                side=POSITION_SIDE_LONG,
                action=EXECUTION_ACTION_OPEN_LONG,
            )

        if state.last_signal == SIGNAL_SELL:
            return self._open_position_if_empty(
                state=state,
                side=POSITION_SIDE_SHORT,
                action=EXECUTION_ACTION_OPEN_SHORT,
            )

        return self._skip_execution(
            state,
            "Нет торгового действия.",
        )