# app/src/telegram/handlers/debug_auto/ui.py
 
from __future__ import annotations

import time
from datetime import datetime, timezone
from decimal import Decimal
from zoneinfo import ZoneInfo

from aiogram.types import InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder

from src.core.config import load_settings
from src.core.types import NumericLike
from src.integrations.exchange.service import ExchangeService
from src.trading.debug.service import DebugTradeService


def debug_auto_keyboard() -> InlineKeyboardMarkup:
    builder = InlineKeyboardBuilder()

    builder.button(text="▶️ Start", callback_data="debug_auto:start")
    builder.button(text="🛑 Stop", callback_data="debug_auto:stop")
    builder.button(text="🟢 LONG", callback_data="debug_auto:long")
    builder.button(text="🔴 SHORT", callback_data="debug_auto:short")
    builder.button(text="🔁 Flip", callback_data="debug_auto:flip")
    builder.button(text="❌ Close", callback_data="debug_auto:close")
    builder.button(text="🔄 Reset", callback_data="debug_auto:reset")

    builder.adjust(2, 3, 2)
    return builder.as_markup()


def build_debug_auto_text() -> str:
    state = DebugTradeService().get_state()
    position = state.position

    parts = [
        "🧪 <b>[DEBUG] Автоторговля</b>",
        "",
        f"<b>Status</b> · {state.status}",
        f"<b>Актив</b> · {_asset_symbol(state.symbol)}",
        f"<b>Стратегия</b> · {state.strategy or '—'}",
        f"<b>Баланс</b> · $ {_format_money_compact(state.allocated_balance_usd)}",
        f"<b>Realized PnL</b> · {_format_signed_usd(state.realized_pnl_usd)}",
        "",
        *_market_snapshot_lines(state.symbol),
        "",
        _signal_line(state),
    ]

    if state.last_signal != "HOLD":
        parts.append(f"<b>Уверенность</b> · {state.last_signal_confidence:.2f}")

    parts.extend(
        [
            f"<b>Decision</b> · {state.decision_status}",
            "",
            "<b>Risk</b>",
            f"SL · {_format_percent(state.stop_loss_percent)}",
            f"TP · {_format_percent(state.take_profit_percent)}",
            f"ML · {_format_usd_or_off(state.max_loss_usd)}",
            f"Max Reserved · {_format_percent(state.max_reserved_balance_percent)}",
        ]
    )

    if state.execution_block_reason or state.execution_size_adjustment_reason:
        parts.extend(
            [
                "",
                f"Blocked · {state.execution_block_reason or '—'}",
                f"Adjusted · {state.execution_size_adjustment_reason or '—'}",
            ]
        )

    parts.append("")

    if position.side == "NONE":
        parts.extend(
            [
                "📭 <b>Позиция не открыта</b>",
                "",
                "Debug runtime изолирован от обычной автоторговли.",
            ]
        )
        return "\n".join(parts)

    side_icon = "🟢" if position.side == "LONG" else "🔴"

    notional = None
    if position.entry_price is not None and position.size is not None:
        notional = position.entry_price * position.size

    reserved = None
    if notional is not None and position.leverage and position.leverage > 0:
        reserved = notional / position.leverage

    parts.extend(
        [
            f"{side_icon} <b>{position.side}</b> · {_asset_symbol(position.symbol)} · {_leverage_text(position.leverage)}",
            "",
            f"<b>Entry</b> · {_format_usd_or_dash(position.entry_price)}",
            f"<b>Size</b> · {_format_crypto_size(position.size)}",
            f"<b>Notional</b> · {_format_usd_or_dash(notional)}",
            f"<b>Reserved</b> · {_format_usd_or_dash(reserved)}",
            f"<b>PnL</b> · {_format_signed_usd(position.unrealized_pnl_usd)}",
            f"<b>Opened</b> · {position.opened_at or '—'}",
            f"<b>Updated</b> · {position.updated_at or '—'}",
            "",
            "Debug runtime изолирован от обычной автоторговли.",
        ]
    )

    return "\n".join(parts)


def _format_updated_at(value: object) -> str:
    if not value:
        return "—"

    if isinstance(value, datetime):
        current = value

        if current.tzinfo is None:
            current = current.replace(tzinfo=timezone.utc)

        try:
            settings = load_settings()

            current = current.astimezone(
                ZoneInfo(settings.tz),
            )
        except Exception:
            current = current.astimezone()

        return current.strftime("%H:%M:%S")

    text = str(value)

    if " " in text:
        return text.rsplit(" ", 1)[-1]

    return text


def _quote_age_seconds(quote: object) -> float | None:
    received_at = getattr(quote, "received_at", None)
    if not isinstance(received_at, datetime):
        return None

    if received_at.tzinfo is None:
        received_at = received_at.replace(tzinfo=timezone.utc)

    return max(
        0.0,
        (
            datetime.now(timezone.utc)
            - received_at.astimezone(timezone.utc)
        ).total_seconds(),
    )


def _market_snapshot_lines(symbol: str | None) -> list[str]:
    if not symbol:
        return [
            "<b>Market</b>",
            "Last · —",
            "Bid · —",
            "Ask · —",
            "Source · —",
            "Age · —",
            "",
            "<b>Execution</b>",
            "Source · —",
            "Age · —",
        ]

    market = None
    execution = None
    error = None

    try:
        market = ExchangeService().get_quote(
            symbol,
            runtime_key="debug_auto",
        )
    except Exception as exc:
        error = str(exc)

    try:
        execution = ExchangeService().get_execution_snapshot(
            symbol,
            runtime_key="debug_auto",
        )
    except Exception as exc:
        if error is None:
            error = str(exc)

    if market is None and execution is None:
        return [
            "<b>Market</b>",
            "Last · —",
            "Bid · —",
            "Ask · —",
            "Source · error",
            f"Error · {error or 'unknown'}",
        ]

    last_price = market.last_price if market else getattr(execution, "last_price", None)
    bid_price = market.bid_price if market else getattr(execution, "bid_price", None)
    ask_price = market.ask_price if market else getattr(execution, "ask_price", None)
    market_source = market.source if market else "—"
    market_age = _quote_age_seconds(market) if market else None

    execution_source = getattr(execution, "source", "—") if execution else "—"
    execution_age = getattr(execution, "age_seconds", None) if execution else None
    execution_fresh = getattr(execution, "is_fresh", None) if execution else None

    return [
        "<b>Market</b>",
        f"Last · {_format_usd_or_dash(last_price)}",
        f"Bid · {_format_usd_or_dash(bid_price)}",
        f"Ask · {_format_usd_or_dash(ask_price)}",
        f"Source · {market_source or '—'}",
        f"Quote age · {_format_age(market_age)}",
        f"Exchange time · {_format_updated_at(market.exchange_timestamp if market else None)}",
        "",
        "<b>Execution</b>",
        f"Source · {execution_source or '—'}",
        f"Quote age · {_format_age(execution_age)}",
        f"Fresh · {_format_bool(execution_fresh)}",
    ]


def _signal_line(state) -> str:
    signal = state.last_signal or "HOLD"

    if signal in {"BUY", "SELL"} and state.is_signal_ready:
        return f"<b>Сигнал</b> {_signal_icon(signal)} {signal} · READY"

    return f"<b>Сигнал</b> {_signal_icon(signal)} {signal} · {_signal_duration_text(state)}"


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

    if started_at is not None:
        total_seconds = max(0, int(time.monotonic() - float(started_at)))
    else:
        total_seconds = max(0, (state.last_signal_repeat_count or 0) * 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 _asset_symbol(symbol: str | None) -> str:
    if not symbol:
        return "—"

    base = symbol.split("_", 1)[0].upper()

    if "/" in base:
        return base.split("/", 1)[0]

    for suffix in ("USDT", "USD", "EUR", "BTC"):
        if base.endswith(suffix) and len(base) > len(suffix):
            return base[: -len(suffix)]

    return base


def _leverage_text(value: float | int | None) -> str:
    if value is None:
        return "x—"

    return f"x{float(value):g}"


def _format_percent(value: float | int | None) -> str:
    if value is None:
        return "off"

    number = float(value)

    if abs(number - round(number)) < 1e-9:
        return f"{int(round(number))}%"

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


def _format_crypto_size(value: float | int | None) -> str:
    if value is None:
        return "—"

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


def _format_money_compact(
    value: float | int | Decimal | None,
) -> str:
    if value is None:
        return "—"

    number = float(value)

    if abs(number - round(number)) < 1e-9:
        return f"{number:,.0f}".replace(",", " ")

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


def _format_usd_or_dash(
    value: float | int | Decimal | None,
) -> str:
    if value is None:
        return "—"

    return f"$ {_format_money_compact(value)}"


def _format_usd_or_off(
    value: float | int | Decimal | None,
) -> str:
    if value is None:
        return "Выкл."

    return f"$ {_format_money_compact(value)}"


def _format_signed_usd(value: float | int | Decimal | None) -> str:
    if value is None:
        return "—"

    amount = float(value)

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

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

    return "$ 0"


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

    try:
        age = max(0.0, float(value))
    except (TypeError, ValueError):
        return "—"

    if age < 1:
        return f"{age:.2f}с"

    if age < 10:
        return f"{age:.1f}с"

    total_seconds = int(age)

    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 _format_bool(value: object) -> str:
    if value is True:
        return "yes"

    if value is False:
        return "no"

    return "—"