# app/src/telegram/handlers/debug.py

from __future__ import annotations

import math
import time

from aiogram import F, Router
from aiogram.types import Message

from src.core.config import load_settings
from src.core.numbers import safe_float
from src.core.types import JsonList, NumericLike
from src.trading.debug.execution import DebugExecutionEngine
from src.trading.debug.service import DebugTradeService
from src.trading.debug.state import DebugTradeState
from src.trading.execution.models import ExecutionDecision


router = Router(name="debug")


def _debug_enabled() -> bool:
    return bool(load_settings().debug_enabled)


def _debug_help_text() -> str:
    return (
        "<b>🧪 Debug commands</b>\n\n"
        "<b>Isolated Debug Runtime:</b>\n"
        "/debug_auto reset\n"
        "/debug_auto off\n"
        "/debug_auto hold 335\n"
        "/debug_auto buy 12 0.74\n"
        "/debug_auto buy_ready 0.88\n"
        "/debug_auto sell 9 0.71\n"
        "/debug_auto sell_ready 0.91\n"
        "/debug_auto long\n"
        "/debug_auto short\n"
        "/debug_auto state\n\n"
        "<b>Isolated Debug Execution:</b>\n"
        "/debug_exec buy — открыть DEBUG LONG\n"
        "/debug_exec sell — открыть DEBUG SHORT\n"
        "/debug_exec flip — перевернуть DEBUG позицию\n"
        "/debug_exec close — закрыть DEBUG позицию\n"
        "/debug_exec process — один цикл DEBUG execution\n"
        "/debug_exec update — обновить DEBUG PnL\n"
        "/debug_exec state — состояние DEBUG runtime\n\n"
        "<b>Legacy aliases:</b>\n"
        "/debug_signal BUY 0.95 3\n"
        "/debug_signal SELL 0.70 2\n"
        "/debug_signal HOLD 0.00 1\n"
        "/debug_ready\n"
        "/debug_state\n\n"
        "⚠️ Все команды работают в изолированном [DEBUG] runtime "
        "и не меняют обычную автоторговлю."
    )


@router.message(F.text == "/debug_help")
async def debug_help(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    await message.answer(_debug_help_text())


@router.message(F.text.startswith("/debug_auto"))
async def debug_auto(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    parts = (message.text or "").split()
    command = parts[1].lower() if len(parts) > 1 else "help"

    service = DebugTradeService()

    if command in {"help", "-h", "--help"}:
        await message.answer(_debug_help_text())
        return

    if command == "reset":
        state = service.reset()

        await message.answer(
            "✅ [DEBUG] Runtime reset\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "off":
        state = service.stop()

        await message.answer(
            "✅ [DEBUG] Runtime stopped\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "state":
        state = service.get_state()
        service.update_market()

        await message.answer(_debug_state_text(state))
        return

    if command == "hold":
        seconds = _parse_int(parts, index=2, default=335)

        state = service.set_signal_duration(
            signal="HOLD",
            seconds=seconds,
            confidence=0.0,
            force_ready=False,
        )

        await message.answer(
            f"✅ [DEBUG] HOLD {seconds}s\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "buy":
        seconds = _parse_int(parts, index=2, default=12)
        confidence = _parse_float(parts, index=3, default=0.74)

        state = service.set_signal_duration(
            signal="BUY",
            seconds=seconds,
            confidence=confidence,
            force_ready=False,
        )

        await message.answer(
            f"✅ [DEBUG] BUY {seconds}s confidence={confidence:.2f}\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "buy_ready":
        confidence = _parse_float(parts, index=2, default=0.88)

        state = service.set_signal_duration(
            signal="BUY",
            seconds=15,
            confidence=confidence,
            force_ready=True,
        )

        await message.answer(
            f"✅ [DEBUG] BUY READY confidence={confidence:.2f}\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "sell":
        seconds = _parse_int(parts, index=2, default=9)
        confidence = _parse_float(parts, index=3, default=0.71)

        state = service.set_signal_duration(
            signal="SELL",
            seconds=seconds,
            confidence=confidence,
            force_ready=False,
        )

        await message.answer(
            f"✅ [DEBUG] SELL {seconds}s confidence={confidence:.2f}\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "sell_ready":
        confidence = _parse_float(parts, index=2, default=0.91)

        state = service.set_signal_duration(
            signal="SELL",
            seconds=15,
            confidence=confidence,
            force_ready=True,
        )

        await message.answer(
            f"✅ [DEBUG] SELL READY confidence={confidence:.2f}\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    if command == "long":
        state, result = service.open_long()

        await message.answer(
            _execution_result_text(
                "OPEN LONG",
                state,
                result,
            )
        )
        return

    if command == "short":
        state, result = service.open_short()

        await message.answer(
            _execution_result_text(
                "OPEN SHORT",
                state,
                result,
            )
        )
        return

    await message.answer(
        f"⛔️ Неизвестная команда: {command}\n\n{_debug_help_text()}"
    )


@router.message(F.text.startswith("/debug_exec"))
async def debug_exec(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    parts = (message.text or "").split()
    command = parts[1].lower() if len(parts) > 1 else "help"

    service = DebugTradeService()

    if command in {"help", "-h", "--help"}:
        await message.answer(_debug_help_text())
        return

    if command == "state":
        state = service.get_state()
        service.update_market()

        await message.answer(_debug_state_text(state))
        return

    if command == "buy":
        state, result = service.open_long()

        await message.answer(
            _execution_result_text(
                "EXEC BUY / LONG",
                state,
                result,
            )
        )
        return

    if command == "sell":
        state, result = service.open_short()

        await message.answer(
            _execution_result_text(
                "EXEC SELL / SHORT",
                state,
                result,
            )
        )
        return

    if command == "flip":
        state, result = service.flip()

        await message.answer(
            _execution_result_text(
                "EXEC AUTO FLIP",
                state,
                result,
            )
        )
        return

    if command == "close":
        state, result = service.close(reason="DEBUG_CLOSE")

        await message.answer(
            _execution_result_text(
                "EXEC CLOSE",
                state,
                result,
            )
        )
        return

    if command == "process":
        state, result = service.process()

        await message.answer(
            _execution_result_text(
                "EXEC PROCESS",
                state,
                result,
            )
        )
        return

    if command == "update":
        state = service.update_market()

        await message.answer(
            "✅ [DEBUG] Market update\n\n"
            f"{_debug_state_text(state)}"
        )
        return

    await message.answer(
        f"⛔️ Неизвестная команда: {command}\n\n{_debug_help_text()}"
    )


@router.message(F.text.startswith("/debug_live"))
async def debug_live(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    await message.answer(
        "⚠️ /debug_live отключён в рамках развязки debug runtime.\n\n"
        "Теперь debug больше не вмешивается в обычную автоторговлю.\n"
        "Используйте:\n\n"
        "/debug_exec buy\n"
        "/debug_exec sell\n"
        "/debug_exec flip\n"
        "/debug_exec close\n\n"
        "Live-мониторинг для изолированного debug будет добавлен "
        "в следующем пакете через отдельный DebugTradeRunner "
        "и отдельный Debug Auto экран."
    )


@router.message(F.text.startswith("/debug_signal"))
async def debug_signal(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    signal, confidence, repeat_count, error = _parse_debug_signal_args(
        message.text,
    )

    if error is not None:
        await message.answer(
            f"⛔️ {error}\n\n{_debug_help_text()}"
        )
        return

    service = DebugTradeService()

    state = service.set_signal(
        signal=signal,
        confidence=confidence,
        repeat_count=repeat_count,
        reason=(
            f"[DEBUG] LEGACY FORCE "
            f"{signal} {confidence:.2f} ×{repeat_count}"
        ),
        force_ready=(
            signal in {"BUY", "SELL"}
            and repeat_count >= 2
        ),
    )

    await message.answer(
        "✅ [DEBUG] Legacy signal forced\n\n"
        f"{_debug_state_text(state)}"
    )


@router.message(F.text == "/debug_ready")
async def debug_ready(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    service = DebugTradeService()

    state = service.set_signal_duration(
        signal="BUY",
        seconds=15,
        confidence=0.95,
        force_ready=True,
    )

    await message.answer(
        "✅ [DEBUG] Legacy READY created\n\n"
        f"{_debug_state_text(state)}"
    )


@router.message(F.text == "/debug_state")
async def debug_state(message: Message) -> None:
    if not _debug_enabled():
        await message.answer("Debug mode выключен.")
        return

    service = DebugTradeService()

    state = service.get_state()
    service.update_market()

    await message.answer(_debug_state_text(state))


def _debug_state_text(
    state: DebugTradeState,
) -> str:
    position = state.position

    duration = _signal_duration_text(state)
    pnl = position.unrealized_pnl_usd

    return (
        "<b>[DEBUG] Auto State</b>\n\n"
        f"Status: {state.status}\n"
        f"Symbol: {state.symbol}\n"
        f"Strategy: {state.strategy}\n"
        f"Risk: {state.risk_percent}\n"
        f"Leverage: {state.leverage}\n"
        f"Allocated: $ {_format_money_compact(state.allocated_balance_usd)}\n"
        f"Realized PnL: {_format_signed_usd(state.realized_pnl_usd)}\n\n"
        f"<b>Signal</b>\n"
        f"Signal: {_signal_icon(state.last_signal)} {state.last_signal}\n"
        f"Duration: {duration}\n"
        f"Repeats: {state.last_signal_repeat_count}\n"
        f"Confidence: {safe_float(state.last_signal_confidence) or 0.0:.2f}\n"
        f"Decision: {state.decision_status}\n"
        f"Ready: {state.is_signal_ready}\n"
        f"Reason: {state.last_signal_reason or '—'}\n\n"
        f"<b>Risk</b>\n"
        f"SL: {_format_percent(state.stop_loss_percent)}\n"
        f"TP: {_format_percent(state.take_profit_percent)}\n"
        f"ML: {_format_usd_or_off(state.max_loss_usd)}\n"
        f"Max Reserved: {_format_percent(state.max_reserved_balance_percent)}\n"
        f"Block: {state.execution_block_reason or '—'}\n"
        f"Adjustment: {state.execution_size_adjustment_reason or '—'}\n\n"
        f"<b>Position</b>\n"
        f"Side: {position.side}\n"
        f"Entry: {_format_usd_or_dash(position.entry_price)}\n"
        f"Size: {_format_crypto_size(position.size)}\n"
        f"Leverage: {_format_leverage(position.leverage)}\n"
        f"PnL: {_format_signed_usd(pnl)}\n"
        f"Opened: {position.opened_at or '—'}\n"
        f"Updated: {position.updated_at or '—'}\n\n"
        "Runtime: isolated [DEBUG]"
    )


def _execution_result_text(
    title: str,
    state: DebugTradeState,
    result: ExecutionDecision,
) -> str:
    return (
        f"✅ [DEBUG] {title}\n\n"
        f"Action: {result.action}\n"
        f"Can execute: {result.can_execute}\n"
        f"Reason: {result.reason}\n\n"
        f"{_debug_state_text(state)}"
    )


def _parse_debug_signal_args(
    raw_text: str | None,
) -> tuple[str, float, int, str | None]:
    parts = (raw_text or "").split()

    signal = parts[1].upper() if len(parts) > 1 else "BUY"

    if signal not in {"BUY", "SELL", "HOLD"}:
        return (
            "BUY",
            0.9,
            2,
            "SIGNAL должен быть BUY, SELL или HOLD.",
        )

    confidence = _parse_float(parts, index=2, default=0.9)

    if confidence < 0 or confidence > 1:
        return (
            "BUY",
            0.9,
            2,
            "CONFIDENCE должен быть от 0.00 до 1.00.",
        )

    repeat_count = _parse_int(parts, index=3, default=2)

    if repeat_count < 1:
        return (
            "BUY",
            0.9,
            2,
            "REPEATS должен быть больше или равен 1.",
        )

    return signal, confidence, repeat_count, None


def _parse_int(
    parts: JsonList,
    *,
    index: int,
    default: int,
) -> int:
    try:
        value = parts[index]
    except (IndexError, TypeError):
        return default

    number = safe_float(value)

    if number is None:
        return default

    return int(number)


def _parse_float(
    parts: JsonList,
    *,
    index: int,
    default: float,
) -> float:
    try:
        value = parts[index]
    except (IndexError, TypeError):
        return default

    number = safe_float(value)

    if number is None:
        return default

    return number


def _signal_duration_text(
    state: DebugTradeState,
) -> str:
    started_at = safe_float(state.signal_started_at)

    if started_at is not None:
        total_seconds = max(
            0,
            int(time.monotonic() - started_at),
        )
    else:
        repeats = state.last_signal_repeat_count or 0
        total_seconds = max(0, int(repeats) * 5)

    hours = total_seconds // 3600
    minutes = (total_seconds % 3600) // 60
    seconds = total_seconds % 60

    if hours > 0:
        return f"{hours}ч {minutes:02d}м"

    if minutes > 0:
        return f"{minutes}м {seconds:02d}с"

    return f"{seconds}с"


def _signal_icon(
    signal: str | None,
) -> str:
    mapping = {
        "BUY": "🟢",
        "SELL": "🔴",
        "HOLD": "🟡",
    }

    return mapping.get(signal or "", "⚪")


def _format_leverage(
    value: NumericLike | None,
) -> str:
    number = safe_float(value)

    if number is None:
        return "x—"

    return f"x{number:g}"


def _format_crypto_size(
    value: NumericLike | None,
) -> str:
    number = safe_float(value)

    if number is None:
        return "—"

    return f"{number:.5f}".rstrip("0").rstrip(".")


def _format_percent(
    value: NumericLike | None,
) -> str:
    number = safe_float(value)

    if number is None:
        return "off"

    if math.isclose(number, round(number), abs_tol=1e-9):
        return f"{int(round(number))}%"

    return f"{number:.2f}".rstrip("0").rstrip(".") + "%"


def _format_money_compact(
    value: NumericLike | None,
) -> str:
    number = safe_float(value)

    if number is None:
        return "—"

    if math.isclose(number, round(number), abs_tol=1e-9):
        return f"{number:,.0f}".replace(",", " ")

    return (
        f"{number:,.2f}"
        .replace(",", " ")
        .rstrip("0")
        .rstrip(".")
    )


def _format_usd_or_dash(
    value: NumericLike | None,
) -> str:
    if safe_float(value) is None:
        return "—"

    return f"$ {_format_money_compact(value)}"


def _format_usd_or_off(
    value: NumericLike | None,
) -> str:
    if safe_float(value) is None:
        return "off"

    return f"$ {_format_money_compact(value)}"


def _format_signed_usd(
    value: NumericLike | None,
) -> str:
    amount = safe_float(value)

    if amount is None:
        return "—"

    if amount > 0:
        return f"🟢 +$ {_format_money_compact(amount)}"

    if amount < 0:
        return f"🔴 −$ {_format_money_compact(abs(amount))}"

    return "$ 0"