# app/src/integrations/exchange/runtime_ui.py

from __future__ import annotations

from src.core.numbers import safe_float
from src.integrations.exchange.models import TimeSyncStatus
from src.integrations.exchange.status import (
    ExchangeRuntimeStatus,
    ExchangeStatusCode,
    build_exchange_error_status,
)


def format_drift_seconds(value: float | int | None) -> str:
    number = safe_float(value)

    if number is None:
        return "—"

    sign = "-" if number < 0 else "+"
    total_seconds = abs(int(round(number)))

    minutes = total_seconds // 60
    seconds = total_seconds % 60

    if minutes > 0:
        return f"{sign} {minutes} мин. {seconds} сек."

    return f"{sign} {seconds} сек."


def build_time_sync_details(sync: TimeSyncStatus) -> str:
    lines = [
        "Проверь настройки времени на:",
        f"Сервер: {sync.hostname}",
    ]

    if sync.local_ip:
        lines.append(f"IP: {sync.local_ip}")

    lines.append("")
    lines.append(f"Время сервера: {sync.local_time}")

    if sync.exchange_time:
        lines.append(f"Время биржи: {sync.exchange_time}")

    lines.append(f"Расхождение: {format_drift_seconds(sync.drift_seconds)}")

    return "\n".join(lines)


def get_time_sync_status_from_service() -> TimeSyncStatus:
    from src.integrations.exchange.service import ExchangeService

    return ExchangeService().get_time_sync_status()


def build_time_sync_details_from_service() -> str:
    return build_time_sync_details(get_time_sync_status_from_service())


def build_exchange_error_ui_parts(
    exc: Exception,
) -> tuple[ExchangeRuntimeStatus, str, str]:
    status = build_exchange_error_status(exc)

    if status.code == ExchangeStatusCode.AUTH_ERROR:
        return (
            status,
            "⛔️ Ошибка доступа к аккаунту",
            "Проверь API-ключ, Secret Key, IP whitelist и права доступа.",
        )

    if status.code == ExchangeStatusCode.TIME_ERROR:
        return (
            status,
            "⛔️ Ошибка времени биржи",
            build_time_sync_details_from_service(),
        )

    if status.code == ExchangeStatusCode.EXCHANGE_UNAVAILABLE:
        return status, "⛔️ Биржа недоступна", ""

    return status, status.ui_line, status.message


def build_runtime_exchange_status(exc: Exception) -> dict[str, object]:
    status = build_exchange_error_status(exc)

    if status.code == ExchangeStatusCode.TIME_ERROR:
        sync = get_time_sync_status_from_service()

        return {
            "code": status.code.value,
            "title": "Ошибка времени биржи",
            "ui_line": "⛔️ Ошибка времени биржи",
            "details": {
                "hostname": sync.hostname,
                "local_ip": sync.local_ip,
                "local_time": sync.local_time,
                "exchange_time": sync.exchange_time,
                "drift_seconds": sync.drift_seconds,
            },
            "reason": status.reason,
            "raw_error": status.raw_error,
        }

    _, title, details = build_exchange_error_ui_parts(exc)

    return {
        "code": status.code.value,
        "title": title.replace("⛔️ ", "").strip(),
        "ui_line": title,
        "details": details,
        "reason": status.reason,
        "raw_error": status.raw_error,
    }


def build_runtime_exchange_alerts(
    *,
    symbol: str | None = None,
    exc: Exception | None = None,
    include_exchange_unavailable: bool = True,
) -> list[dict[str, object]]:
    from src.integrations.exchange.service import ExchangeService

    alerts: list[dict[str, object]] = []
    service = ExchangeService()

    def add_alert(alert: dict[str, object] | None) -> None:
        if not alert:
            return

        code = str(alert.get("code") or "")
        reason = str(alert.get("reason") or "")

        for existing in alerts:
            if (
                str(existing.get("code") or "") == code
                and str(existing.get("reason") or "") == reason
            ):
                return

        alerts.append(alert)

    if exc is not None:
        add_alert(build_runtime_exchange_status(exc))

    try:
        runtime_status = service.get_symbol_runtime_status(symbol)
    except Exception as status_exc:
        if include_exchange_unavailable:
            add_alert(build_runtime_exchange_status(status_exc))
    else:
        if include_exchange_unavailable and not runtime_status.is_available:
            add_alert(
                build_runtime_exchange_status(
                    Exception(runtime_status.raw_error or runtime_status.message)
                )
            )

    try:
        time_sync = service.get_time_sync_status()
    except Exception as time_exc:
        add_alert(build_runtime_exchange_status(time_exc))
    else:
        if not time_sync.ok:
            add_alert(
                {
                    "code": ExchangeStatusCode.TIME_ERROR.value,
                    "title": "Ошибка времени биржи",
                    "ui_line": "⛔️ Ошибка времени биржи",
                    "details": {
                        "hostname": time_sync.hostname,
                        "local_ip": time_sync.local_ip,
                        "local_time": time_sync.local_time,
                        "exchange_time": time_sync.exchange_time,
                        "drift_seconds": time_sync.drift_seconds,
                    },
                    "reason": "time_error",
                    "raw_error": time_sync.message,
                }
            )

    try:
        private_auth_health = service.get_private_auth_health()
    except Exception as auth_exc:
        add_alert(build_runtime_exchange_status(auth_exc))
    else:
        if not private_auth_health.ok:
            add_alert(
                build_runtime_exchange_status(
                    Exception(private_auth_health.message)
                )
            )

    priority = {
        ExchangeStatusCode.EXCHANGE_UNAVAILABLE.value: 10,
        ExchangeStatusCode.TIME_ERROR.value: 20,
        ExchangeStatusCode.AUTH_ERROR.value: 30,
    }

    alerts.sort(
        key=lambda alert: priority.get(
            str(alert.get("code") or ""),
            999,
        )
    )

    return alerts


def format_runtime_exchange_alert(alert: dict[str, object]) -> str:
    title = str(
        alert.get("ui_line")
        or alert.get("title")
        or "⛔️ Ошибка биржи"
    ).strip()

    details = alert.get("details")
    code = str(alert.get("code") or "")

    lines = [title]

    if isinstance(details, dict):
        lines.append("Проверь настройки времени на:")

        hostname = details.get("hostname")
        local_ip = details.get("local_ip")
        local_time = details.get("local_time")
        exchange_time = details.get("exchange_time")
        drift_seconds = details.get("drift_seconds")

        if hostname:
            lines.append(f"• Сервер: {hostname}")

        if local_ip:
            lines.append(f"• IP: {local_ip}")

        if local_time:
            lines.append(f"• Время сервера: {local_time}")

        if exchange_time:
            lines.append(f"• Время биржи: {exchange_time}")

        lines.append(f"• Расхождение: {format_drift_seconds(drift_seconds)}")

        return "\n".join(lines).strip()

    if code == ExchangeStatusCode.AUTH_ERROR.value:
        lines.extend([
            "Проверь:",
            "• API-ключ, Secret Key",
            "• IP whitelist и права доступа",
        ])
        return "\n".join(lines).strip()

    details_text = str(details or "").strip()

    if details_text:
        lines.append(details_text)

    return "\n".join(lines).strip()


def format_runtime_exchange_alerts(alerts: list[dict[str, object]]) -> str:
    return "\n\n".join(
        block
        for block in (
            format_runtime_exchange_alert(alert)
            for alert in alerts
        )
        if block.strip()
    ).strip()


def build_runtime_exchange_alert_lines(
    *,
    symbol: str | None = None,
    include_exchange_unavailable: bool = True,
) -> list[str]:
    alerts = build_runtime_exchange_alerts(
        symbol=symbol,
        include_exchange_unavailable=include_exchange_unavailable,
    )

    lines: list[str] = []

    for alert in alerts:
        line = str(alert.get("ui_line") or alert.get("title") or "").strip()

        if line and line not in lines:
            lines.append(line)

    return lines