# app/src/telegram/handlers/auto/risk.py

from __future__ import annotations

from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.types import (
    CallbackQuery,
    InlineKeyboardMarkup,
    Message,
    InaccessibleMessage,
)

from src.core.numbers import safe_float
from src.core.types import JsonDict, NumericLike
from src.trading.auto.runner import AutoTradeRunner
from src.trading.auto.service import AutoTradeService
from src.trading.journal.service import JournalService


router = Router(name="auto_risk")


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

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

    return message


class AutoRiskStates(StatesGroup):
    waiting_stop_loss = State()
    waiting_take_profit = State()
    waiting_max_loss = State()


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

    if number is None:
        return "—"

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

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


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

    if number is None:
        return "off"

    return f"{_format_number(number)}%"


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

    if number is None:
        return "off"

    return f"{_format_number(number)} USD"


def _rule_icon(value: NumericLike | None) -> str:
    return "✅" if safe_float(value) is not None else "⚠️"


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

    builder.button(text="🛑 SL", callback_data="auto:risk:set_sl")
    builder.button(text="🎯 TP", callback_data="auto:risk:set_tp")
    builder.button(text="💸 ML", callback_data="auto:risk:set_ml")
    builder.button(text="🤖 Автоторговля", callback_data="auto:home")
    builder.button(text="⬅️ Назад", callback_data="settings:auto")
    builder.button(text="♻️ Сбросить", callback_data="auto:risk:reset")

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


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

    active_count = sum(
        value is not None
        for value in (
            state.stop_loss_percent,
            state.take_profit_percent,
            state.max_loss_usd,
        )
    )

    status = "🟢 Активна" if active_count else "⚪ Выключена"

    return (
        "<b>🧯 Защита позиции</b>\n\n"
        "<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
        f"Статус защиты: {status}\n"
        f"Активных правил: {active_count}/3\n\n"
        f"{_rule_icon(state.stop_loss_percent)} Stop Loss · {_format_percent(state.stop_loss_percent)}\n"
        f"{_rule_icon(state.take_profit_percent)} Take Profit · {_format_percent(state.take_profit_percent)}\n"
        f"{_rule_icon(state.max_loss_usd)} Max Loss · {_format_usd(state.max_loss_usd)}\n"
    )


async def _render_risk_screen(
    callback: CallbackQuery,
) -> None:
    AutoTradeRunner.set_current_screen("auto_risk")

    message = _require_message(callback)

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

    _unregister_auto_screen_message(callback)

    await message.edit_text(
        _risk_text(),
        reply_markup=_risk_keyboard(),
    )

    await callback.answer()


async def _render_risk_screen_by_message(
    message: Message,
    *,
    state: FSMContext,
) -> None:
    AutoTradeRunner.set_current_screen("auto_risk")

    bot = message.bot

    if bot is None:
        return

    data: JsonDict = await state.get_data()

    raw_chat_id = data.get("risk_chat_id")
    raw_message_id = data.get("risk_message_id")

    if not isinstance(raw_chat_id, int) or not isinstance(raw_message_id, int):
        await message.answer(
            _risk_text(),
            reply_markup=_risk_keyboard(),
        )
        return

    await bot.edit_message_text(
        chat_id=raw_chat_id,
        message_id=raw_message_id,
        text=_risk_text(),
        reply_markup=_risk_keyboard(),
    )


async def _remember_risk_screen(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    message = _require_message(callback)

    if message is None:
        return

    await state.update_data(
        risk_chat_id=message.chat.id,
        risk_message_id=message.message_id,
    )


def _unregister_auto_screen_message(
    callback: CallbackQuery,
) -> None:
    message = _require_message(callback)

    if message is None:
        return

    AutoTradeRunner.unregister_screen(
        chat_id=message.chat.id,
        message_id=message.message_id,
    )
    

def _risk_payload(**values: object) -> JsonDict:
    return dict(values)


def _parse_positive_or_none(
    raw_text: str | None,
) -> float | None:
    value_text = (raw_text or "").strip().replace(",", ".")

    if value_text.lower() in {
        "0",
        "0.0",
        "off",
        "-",
    }:
        return None

    value = safe_float(value_text)

    if value is None:
        raise ValueError

    if value <= 0:
        return None

    return value


def _validate_percent(
    value: NumericLike | None,
) -> bool:
    number = safe_float(value)

    if number is None:
        return True

    return 0 < number <= 100


def _validate_max_loss(
    value: NumericLike | None,
) -> bool:
    number = safe_float(value)

    if number is None:
        return True

    return 0 < number <= 10000


def _log_risk_updated(action: str) -> None:
    state = AutoTradeService().get_state()

    try:
        JournalService().log_ui_info(
            event_type="risk_settings_updated",
            message=(
                "Параметры защиты позиции изменены: "
                f"SL={_format_percent(state.stop_loss_percent)}, "
                f"TP={_format_percent(state.take_profit_percent)}, "
                f"ML={_format_usd(state.max_loss_usd)}."
            ),
            screen="auto",
            action=action,
            payload=_risk_payload(
                stop_loss_percent=state.stop_loss_percent,
                take_profit_percent=state.take_profit_percent,
                max_loss_usd=state.max_loss_usd,
            ),
        )
    except Exception:
        pass


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


@router.callback_query(F.data == "settings:auto_risk_controls")
async def open_auto_risk_from_settings(callback: CallbackQuery, state: FSMContext) -> None:
    await state.clear()
    await _render_risk_screen(callback)


@router.callback_query(F.data == "auto:risk:set_sl")
async def ask_stop_loss(callback: CallbackQuery, state: FSMContext) -> None:
    AutoTradeRunner.set_current_screen("auto_risk")
    _unregister_auto_screen_message(callback)

    message = _require_message(callback)

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

    await state.set_state(AutoRiskStates.waiting_stop_loss)
    await _remember_risk_screen(callback, state)

    await message.edit_text(
        "<b>Stop Loss</b>\n\n"
        "<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
        "Введите Stop Loss в процентах.\n"
        "Например: <code>1</code>, <code>0.5</code>, <code>0,5</code>\n\n"
        "отключить параметр - <code>0</code>"
    )

    await callback.answer()


@router.callback_query(F.data == "auto:risk:set_tp")
async def ask_take_profit(callback: CallbackQuery, state: FSMContext) -> None:
    AutoTradeRunner.set_current_screen("auto_risk")
    _unregister_auto_screen_message(callback)

    message = _require_message(callback)

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

    await state.set_state(AutoRiskStates.waiting_take_profit)
    await _remember_risk_screen(callback, state)

    await message.edit_text(
        "<b>Take Profit</b>\n\n"
        "<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
        "Введите Take Profit в процентах.\n"
        "Например: <code>2</code>, <code>1.5</code>, <code>1,5</code>\n\n"
        "отключить параметр - <code>0</code>"
    )

    await callback.answer()


@router.callback_query(F.data == "auto:risk:set_ml")
async def ask_max_loss(callback: CallbackQuery, state: FSMContext) -> None:
    AutoTradeRunner.set_current_screen("auto_risk")
    _unregister_auto_screen_message(callback)

    message = _require_message(callback)

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

    await state.set_state(AutoRiskStates.waiting_max_loss)
    await _remember_risk_screen(callback, state)

    await message.edit_text(
        "<b>Maximum Loss</b>\n\n"
        "<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
        "Введите максимальный paper-убыток в USD.\n"
        "Например: <code>100</code>, <code>50.5</code>, <code>50,5</code>\n\n"
        "отключить параметр - <code>0</code>"
    )

    await callback.answer()


@router.callback_query(F.data == "auto:risk:reset")
async def reset_risk(callback: CallbackQuery, state: FSMContext) -> None:
    await state.clear()
    AutoTradeRunner.set_current_screen("auto_risk")
    _unregister_auto_screen_message(callback)

    message = _require_message(callback)

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

    service = AutoTradeService()
    service.set_stop_loss_percent(None)
    service.set_take_profit_percent(None)
    service.set_max_loss_usd(None)

    _log_risk_updated("risk_reset")

    await message.edit_text(
        _risk_text(),
        reply_markup=_risk_keyboard(),
    )

    await callback.answer("Risk Controls сброшены")


@router.message(AutoRiskStates.waiting_stop_loss)
async def set_stop_loss(message: Message, state: FSMContext) -> None:
    try:
        value = _parse_positive_or_none(message.text)
    except ValueError:
        await message.answer("Введите число. Например: 1, 0.5 или 0 для отключения.")
        return

    if not _validate_percent(value):
        await message.answer("Stop Loss должен быть от 0 до 100%.")
        return

    AutoTradeService().set_stop_loss_percent(value)
    _log_risk_updated("set_stop_loss")

    await _render_risk_screen_by_message(
        message,
        state=state,
    )
    await state.clear()


@router.message(AutoRiskStates.waiting_take_profit)
async def set_take_profit(message: Message, state: FSMContext) -> None:
    try:
        value = _parse_positive_or_none(message.text)
    except ValueError:
        await message.answer("Введите число. Например: 2, 1.5 или 0 для отключения.")
        return

    if not _validate_percent(value):
        await message.answer("Take Profit должен быть от 0 до 100%.")
        return

    AutoTradeService().set_take_profit_percent(value)
    _log_risk_updated("set_take_profit")

    await _render_risk_screen_by_message(
        message,
        state=state,
    )
    await state.clear()


@router.message(AutoRiskStates.waiting_max_loss)
async def set_max_loss(message: Message, state: FSMContext) -> None:
    try:
        value = _parse_positive_or_none(message.text)
    except ValueError:
        await message.answer("Введите число. Например: 100, 50.5 или 0 для отключения.")
        return

    if not _validate_max_loss(value):
        await message.answer("Max Loss должен быть от 0 до 10000 USD.")
        return

    AutoTradeService().set_max_loss_usd(value)
    _log_risk_updated("set_max_loss")

    await _render_risk_screen_by_message(
        message,
        state=state,
    )
    await state.clear()