# app/src/telegram/handlers/auto/main.py
 
from __future__ import annotations

from aiogram import F, Router
from aiogram.exceptions import (
    TelegramBadRequest,
    TelegramNetworkError,
    TelegramRetryAfter,
)
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InaccessibleMessage, Message

from src.telegram.handlers.auto.ui import (
    auto_diagnostics_keyboard,
    auto_keyboard,
    build_auto_text,
    is_auto_configured,
    _auto_block_reason,
)
from src.telegram.handlers.system import open_auto_settings
from src.telegram.live.active_screen import ActiveScreenManager
from src.trading.auto.runner import AutoTradeRunner
from src.trading.auto.service import AutoTradeService
from src.trading.diagnostics.formatter import SemanticDiagnosticFormatter
from src.trading.diagnostics.snapshot import SemanticDiagnosticSnapshotBuilder


router = Router(name="auto")


def _require_message(
    callback: CallbackQuery,
) -> Message | None:
    message = callback.message

    if (
        message is None
        or isinstance(message, InaccessibleMessage)
    ):
        return None

    return message


async def _safe_edit_text(
    message: Message,
    text: str,
    *,
    reply_markup,
) -> bool:
    try:
        await message.edit_text(
            text,
            reply_markup=reply_markup,
        )
        return True

    except TelegramBadRequest as exc:
        if "message is not modified" in str(exc).lower():
            return True
        raise

    except TelegramRetryAfter:
        return False

    except TelegramNetworkError:
        return False
    

async def _safe_answer(
    message: Message,
    text: str,
    *,
    reply_markup,
) -> Message | None:
    try:
        return await message.answer(
            text,
            reply_markup=reply_markup,
        )

    except TelegramRetryAfter:
        return None

    except TelegramNetworkError:
        return None
    

async def render_auto_screen(
    target_message: Message,
    *,
    edit_mode: bool,
) -> None:
    text = build_auto_text()

    if edit_mode:
        async with AutoTradeRunner.edit_lock():
            bot = target_message.bot

            if bot is not None:
                AutoTradeRunner.register_screen(
                    bot=bot,
                    chat_id=target_message.chat.id,
                    message_id=target_message.message_id,
                    render_text=build_auto_text,
                    render_markup=auto_keyboard,
                )

                ActiveScreenManager.register(
                    screen="auto",
                    message=target_message,
                )

            updated = await _safe_edit_text(
                target_message,
                text,
                reply_markup=auto_keyboard(),
            )

            if not updated:
                return

        return

    sent_message = await _safe_answer(
        target_message,
        text,
        reply_markup=auto_keyboard(),
    )

    if sent_message is None:
        return
    bot = sent_message.bot

    if bot is None:
        return

    AutoTradeRunner.register_screen(
        bot=bot,
        chat_id=sent_message.chat.id,
        message_id=sent_message.message_id,
        render_text=build_auto_text,
        render_markup=auto_keyboard,
    )

    ActiveScreenManager.register(
        screen="auto",
        message=sent_message,
    )


async def _prepare_auto_from_message(message: Message) -> bool:
    bot = message.bot

    if bot is None:
        return False

    await ActiveScreenManager.prepare_new_screen(
        screen="auto",
        bot=bot,
        chat_id=message.chat.id,
    )

    return True


async def _prepare_auto_from_callback(callback: CallbackQuery) -> bool:
    message = _require_message(callback)

    if message is None:
        await callback.answer("Сообщение недоступно", show_alert=True)
        return False

    bot = message.bot

    if bot is None:
        await callback.answer("Bot недоступен", show_alert=True)
        return False

    await ActiveScreenManager.prepare_new_screen(
        screen="auto",
        bot=bot,
        chat_id=message.chat.id,
        keep_message_id=message.message_id,
    )

    return True


def build_auto_diagnostics_text() -> str:
    service = AutoTradeService()
    state = service.get_state()

    snapshot = SemanticDiagnosticSnapshotBuilder().build(
        state,
        is_configured=is_auto_configured(state),
    )

    return SemanticDiagnosticFormatter().format(snapshot)


async def render_auto_diagnostics_screen(
    target_message: Message,
) -> None:
    text = build_auto_diagnostics_text()

    async with AutoTradeRunner.edit_lock():
        bot = target_message.bot

        if bot is not None:
            AutoTradeRunner.register_screen(
                bot=bot,
                chat_id=target_message.chat.id,
                message_id=target_message.message_id,
                render_text=build_auto_diagnostics_text,
                render_markup=auto_diagnostics_keyboard,
            )

            ActiveScreenManager.register(
                screen="auto_diagnostics",
                message=target_message,
            )

        try:
            await target_message.edit_text(
                text,
                reply_markup=auto_diagnostics_keyboard(),
            )
        except TelegramBadRequest as exc:
            error_text = str(exc).lower()

            if "message to edit not found" in error_text:
                sent_message = await _safe_answer(
                    target_message,
                    text,
                    reply_markup=auto_diagnostics_keyboard(),
                )

                if sent_message is None:
                    return

                bot = sent_message.bot

                if bot is None:
                    return

                AutoTradeRunner.register_screen(
                    bot=bot,
                    chat_id=sent_message.chat.id,
                    message_id=sent_message.message_id,
                    render_text=build_auto_diagnostics_text,
                    render_markup=auto_diagnostics_keyboard,
                )

                ActiveScreenManager.register(
                    screen="auto_diagnostics",
                    message=sent_message,
                )
                return

            if "message is not modified" in error_text:
                return

            raise


@router.message(F.text.in_({"🤖 Автоторговля", "🤖 Авто"}))
async def open_auto(message: Message, state: FSMContext) -> None:
    await state.clear()

    if not await _prepare_auto_from_message(message):
        return

    await render_auto_screen(message, edit_mode=False)


@router.callback_query(F.data == "auto:home")
async def open_auto_from_callback(callback: CallbackQuery, state: FSMContext) -> None:
    await state.clear()

    if not await _prepare_auto_from_callback(callback):
        return

    message = _require_message(callback)

    if message is None:
        await callback.answer("Сообщение недоступно", show_alert=True)
        return

    await render_auto_screen(message, edit_mode=True)
    await callback.answer()


@router.callback_query(F.data == "auto:start_blocked")
async def auto_start_blocked(callback: CallbackQuery) -> None:
    reason = _auto_block_reason() or "Запуск сейчас недоступен"

    await callback.answer(
        reason.replace("⛔️ ", ""),
        show_alert=True,
    )


@router.callback_query(F.data == "auto:start")
async def auto_start(callback: CallbackQuery) -> None:
    service = AutoTradeService()
    state = service.get_state()

    if not is_auto_configured(state):
        await callback.answer(
            "Сначала настрой параметры автоторговли",
            show_alert=True,
        )

        if _require_message(callback) is not None:
            await open_auto_settings(callback)

        return

    block_reason = _auto_block_reason()

    if block_reason:
        await callback.answer(
            block_reason.replace("⛔️ ", "").replace("⛔ ", ""),
            show_alert=True,
        )

        if await _prepare_auto_from_callback(callback):
            message = _require_message(callback)

            if message is not None:
                await render_auto_screen(message, edit_mode=True)

        return

    _, message_text = service.start()

    if await _prepare_auto_from_callback(callback):
        message = _require_message(callback)

        if message is not None:
            await render_auto_screen(message, edit_mode=True)

    AutoTradeRunner.start()

    await callback.answer(message_text)


@router.callback_query(F.data == "auto:observe")
async def auto_observe(callback: CallbackQuery) -> None:
    service = AutoTradeService()
    state = service.get_state()

    if not is_auto_configured(state):
        await callback.answer(
            "Сначала настрой параметры автоторговли",
            show_alert=True,
        )

        if _require_message(callback) is not None:
            await open_auto_settings(callback)

        return

    _, message_text = service.observe()

    if await _prepare_auto_from_callback(callback):
        message = _require_message(callback)

        if message is not None:
            await render_auto_screen(message, edit_mode=True)

    AutoTradeRunner.start()

    await callback.answer(message_text)


@router.callback_query(F.data == "auto:stop")
async def auto_stop(callback: CallbackQuery) -> None:
    service = AutoTradeService()
    _, message_text = service.stop()

    AutoTradeRunner.stop()

    if await _prepare_auto_from_callback(callback):
        message = _require_message(callback)

        if message is not None:
            await render_auto_screen(message, edit_mode=True)

    await callback.answer(message_text)


@router.callback_query(F.data == "auto:diagnostics")
async def open_auto_diagnostics(callback: CallbackQuery) -> None:
    service = AutoTradeService()
    state = service.get_state()

    if str(state.status or "").upper() not in {"RUNNING", "OBSERVING"}:
        await callback.answer(
            "Диагностика доступна только после запуска или в режиме наблюдения",
            show_alert=True,
        )
        return

    message = _require_message(callback)

    if message is None:
        await callback.answer("Сообщение недоступно", show_alert=True)
        return

    bot = message.bot

    if bot is None:
        await callback.answer("Bot недоступен", show_alert=True)
        return

    await ActiveScreenManager.prepare_new_screen(
        screen="auto_diagnostics",
        bot=bot,
        chat_id=message.chat.id,
        keep_message_id=message.message_id,
    )

    await render_auto_diagnostics_screen(message)
    await callback.answer()