import html
from datetime import UTC, datetime
from typing import Any

import structlog
from aiogram import types
from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.database.database import AsyncSessionLocal
from app.database.models import User
from app.keyboards.inline import get_back_keyboard
from app.localization.texts import get_texts
from app.services.payment_service import PaymentService
from app.states import BalanceStates
from app.utils.decorators import error_handler


logger = structlog.get_logger(__name__)


def _get_available_pal24_methods() -> list[str]:
    methods: list[str] = []
    if settings.is_pal24_sbp_button_visible():
        methods.append('sbp')
    if settings.is_pal24_card_button_visible():
        methods.append('card')
    if not methods:
        methods.append('sbp')
    return methods


async def _send_pal24_payment_message(
    message: types.Message,
    db_user: User,
    db: AsyncSession,
    amount_kopeks: int,
    payment_method: str,
    state: FSMContext,
) -> None:
    texts = get_texts(db_user.language)

    try:
        payment_service = PaymentService(message.bot)
        payment_result = await payment_service.create_pal24_payment(
            db=db,
            user_id=db_user.id,
            amount_kopeks=amount_kopeks,
            description=settings.get_balance_payment_description(amount_kopeks, telegram_user_id=db_user.telegram_id),
            language=db_user.language,
            payment_method=payment_method,
        )

        if not payment_result:
            await message.answer(
                texts.t(
                    'PAL24_PAYMENT_ERROR',
                    '❌ Ошибка создания платежа PayPalych. Попробуйте позже или обратитесь в поддержку.',
                )
            )
            await state.clear()
            return

        sbp_url = payment_result.get('sbp_url') or payment_result.get('transfer_url')
        card_url = payment_result.get('card_url')
        fallback_url = payment_result.get('link_page_url') or payment_result.get('link_url')

        if not (sbp_url or card_url or fallback_url):
            await message.answer(
                texts.t(
                    'PAL24_PAYMENT_ERROR',
                    '❌ Ошибка создания платежа PayPalych. Попробуйте позже или обратитесь в поддержку.',
                )
            )
            await state.clear()
            return

        if not sbp_url:
            sbp_url = fallback_url

        bill_id = payment_result.get('bill_id')
        local_payment_id = payment_result.get('local_payment_id')

        pay_buttons: list[list[types.InlineKeyboardButton]] = []
        steps: list[str] = []
        step_counter = 1

        default_sbp_text = texts.t(
            'PAL24_SBP_PAY_BUTTON',
            '🏦 Оплатить через PayPalych (СБП)',
        )
        sbp_button_text = settings.get_pal24_sbp_button_text(default_sbp_text)

        if sbp_url and settings.is_pal24_sbp_button_visible():
            pay_buttons.append(
                [
                    types.InlineKeyboardButton(
                        text=sbp_button_text,
                        url=sbp_url,
                    )
                ]
            )
            steps.append(
                texts.t(
                    'PAL24_INSTRUCTION_BUTTON',
                    '{step}. Нажмите кнопку «{button}»',
                ).format(step=step_counter, button=html.escape(sbp_button_text))
            )
            step_counter += 1

        default_card_text = texts.t(
            'PAL24_CARD_PAY_BUTTON',
            '💳 Оплатить банковской картой (PayPalych)',
        )
        card_button_text = settings.get_pal24_card_button_text(default_card_text)

        if card_url and card_url != sbp_url and settings.is_pal24_card_button_visible():
            pay_buttons.append(
                [
                    types.InlineKeyboardButton(
                        text=card_button_text,
                        url=card_url,
                    )
                ]
            )
            steps.append(
                texts.t(
                    'PAL24_INSTRUCTION_BUTTON',
                    '{step}. Нажмите кнопку «{button}»',
                ).format(step=step_counter, button=html.escape(card_button_text))
            )
            step_counter += 1

        if not pay_buttons and fallback_url and settings.is_pal24_sbp_button_visible():
            pay_buttons.append(
                [
                    types.InlineKeyboardButton(
                        text=sbp_button_text,
                        url=fallback_url,
                    )
                ]
            )
            steps.append(
                texts.t(
                    'PAL24_INSTRUCTION_BUTTON',
                    '{step}. Нажмите кнопку «{button}»',
                ).format(step=step_counter, button=html.escape(sbp_button_text))
            )
            step_counter += 1

        follow_template = texts.t(
            'PAL24_INSTRUCTION_FOLLOW',
            '{step}. Следуйте подсказкам платёжной системы',
        )
        steps.append(follow_template.format(step=step_counter))
        step_counter += 1

        confirm_template = texts.t(
            'PAL24_INSTRUCTION_CONFIRM',
            '{step}. Подтвердите перевод',
        )
        steps.append(confirm_template.format(step=step_counter))
        step_counter += 1

        success_template = texts.t(
            'PAL24_INSTRUCTION_COMPLETE',
            '{step}. Средства зачислятся автоматически',
        )
        steps.append(success_template.format(step=step_counter))

        message_template = texts.t(
            'PAL24_PAYMENT_INSTRUCTIONS',
            (
                '🏦 <b>Оплата через PayPalych</b>\n\n'
                '💰 Сумма: {amount}\n'
                '🆔 ID счета: {bill_id}\n\n'
                '📱 <b>Инструкция:</b>\n{steps}\n\n'
                '❓ Если возникнут проблемы, обратитесь в {support}'
            ),
        )

        keyboard_rows = pay_buttons + [
            [
                types.InlineKeyboardButton(
                    text=texts.t('CHECK_STATUS_BUTTON', '📊 Проверить статус'),
                    callback_data=f'check_pal24_{local_payment_id}',
                )
            ],
            [types.InlineKeyboardButton(text=texts.BACK, callback_data='balance_topup')],
        ]

        keyboard = types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows)

        message_text = message_template.format(
            amount=settings.format_price(amount_kopeks),
            bill_id=bill_id,
            steps='\n'.join(steps),
            support=settings.get_support_contact_display_html(),
        )

        invoice_message = await message.answer(
            message_text,
            reply_markup=keyboard,
            parse_mode='HTML',
        )

        try:
            from app.services import payment_service as payment_module

            payment = await payment_module.get_pal24_payment_by_id(db, local_payment_id)
            if payment:
                metadata = dict(getattr(payment, 'metadata_json', {}) or {})
                metadata['invoice_message'] = {
                    'chat_id': invoice_message.chat.id,
                    'message_id': invoice_message.message_id,
                }
                await db.execute(
                    update(payment.__class__)
                    .where(payment.__class__.id == payment.id)
                    .values(metadata_json=metadata, updated_at=datetime.now(UTC))
                )
                await db.commit()
        except Exception as error:  # pragma: no cover - diagnostics
            logger.warning('Не удалось сохранить сообщение PayPalych', error=error)

        await state.update_data(
            pal24_invoice_message_id=invoice_message.message_id,
            pal24_invoice_chat_id=invoice_message.chat.id,
        )

        await state.clear()

        logger.info(
            'Создан PayPalych счет для пользователя : ₽, ID метод',
            telegram_id=db_user.telegram_id,
            amount_kopeks=amount_kopeks / 100,
            bill_id=bill_id,
            payment_method=payment_method,
        )

    except Exception as error:
        logger.error('Ошибка создания PayPalych платежа', error=error)
        await message.answer(
            texts.t(
                'PAL24_PAYMENT_ERROR',
                '❌ Ошибка создания платежа PayPalych. Попробуйте позже или обратитесь в поддержку.',
            )
        )
        await state.clear()


@error_handler
async def start_pal24_payment(
    callback: types.CallbackQuery,
    db_user: User,
    state: FSMContext,
):
    texts = get_texts(db_user.language)

    # Проверка ограничения на пополнение
    if getattr(db_user, 'restriction_topup', False):
        reason = getattr(db_user, 'restriction_reason', None) or 'Действие ограничено администратором'
        support_url = settings.get_support_contact_url()
        keyboard = []
        if support_url:
            keyboard.append([types.InlineKeyboardButton(text='🆘 Обжаловать', url=support_url)])
        keyboard.append([types.InlineKeyboardButton(text=texts.BACK, callback_data='menu_balance')])

        await callback.message.edit_text(
            f'🚫 <b>Пополнение ограничено</b>\n\n{reason}\n\n'
            'Если вы считаете это ошибкой, вы можете обжаловать решение.',
            reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard),
        )
        await callback.answer()
        return

    if not settings.is_pal24_enabled():
        await callback.answer('❌ Оплата через PayPalych временно недоступна', show_alert=True)
        return

    # Формируем текст сообщения в зависимости от доступных способов оплаты
    if settings.is_pal24_sbp_button_visible() and settings.is_pal24_card_button_visible():
        payment_methods_text = 'СБП и банковской картой'
    elif settings.is_pal24_sbp_button_visible():
        payment_methods_text = 'СБП'
    elif settings.is_pal24_card_button_visible():
        payment_methods_text = 'банковской картой'
    else:
        # Если обе кнопки отключены, используем общий текст
        payment_methods_text = 'доступными способами'

    message_text = texts.t(
        'PAL24_TOPUP_PROMPT',
        (
            f'🏦 <b>Оплата через PayPalych ({payment_methods_text})</b>\n\n'
            'Введите сумму для пополнения от 100 до 1 000 000 ₽.\n'
            f'Оплата проходит через PayPalych ({payment_methods_text}).'
        ),
    )

    keyboard = get_back_keyboard(db_user.language)

    if settings.is_quick_amount_buttons_enabled():
        from .main import get_quick_amount_buttons

        quick_amount_buttons = await get_quick_amount_buttons(db_user.language, db_user)
        if quick_amount_buttons:
            keyboard.inline_keyboard = quick_amount_buttons + keyboard.inline_keyboard

    await callback.message.edit_text(
        message_text,
        reply_markup=keyboard,
        parse_mode='HTML',
    )

    await state.set_state(BalanceStates.waiting_for_amount)
    await state.update_data(
        payment_method='pal24',
        pal24_prompt_message_id=callback.message.message_id,
        pal24_prompt_chat_id=callback.message.chat.id,
    )
    await callback.answer()


@error_handler
async def process_pal24_payment_amount(
    message: types.Message,
    db_user: User,
    db: AsyncSession,
    amount_kopeks: int,
    state: FSMContext,
):
    texts = get_texts(db_user.language)

    # Проверка ограничения на пополнение
    if getattr(db_user, 'restriction_topup', False):
        reason = getattr(db_user, 'restriction_reason', None) or 'Действие ограничено администратором'
        support_url = settings.get_support_contact_url()
        keyboard = []
        if support_url:
            keyboard.append([types.InlineKeyboardButton(text='🆘 Обжаловать', url=support_url)])
        keyboard.append([types.InlineKeyboardButton(text=texts.BACK, callback_data='menu_balance')])

        await message.answer(
            f'🚫 <b>Пополнение ограничено</b>\n\n{reason}\n\n'
            'Если вы считаете это ошибкой, вы можете обжаловать решение.',
            reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard),
            parse_mode='HTML',
        )
        await state.clear()
        return

    if not settings.is_pal24_enabled():
        await message.answer('❌ Оплата через PayPalych временно недоступна')
        return

    if amount_kopeks < settings.PAL24_MIN_AMOUNT_KOPEKS:
        min_rubles = settings.PAL24_MIN_AMOUNT_KOPEKS / 100
        await message.answer(f'❌ Минимальная сумма для оплаты через PayPalych: {min_rubles:.0f} ₽')
        return

    if amount_kopeks > settings.PAL24_MAX_AMOUNT_KOPEKS:
        max_rubles = settings.PAL24_MAX_AMOUNT_KOPEKS / 100
        await message.answer(f'❌ Максимальная сумма для оплаты через PayPalych: {max_rubles:,.0f} ₽'.replace(',', ' '))
        return

    available_methods = _get_available_pal24_methods()

    state_data = await state.get_data()
    prompt_message_id = state_data.get('pal24_prompt_message_id')
    prompt_chat_id = state_data.get('pal24_prompt_chat_id', message.chat.id)

    try:
        await message.delete()
    except Exception as delete_error:  # pragma: no cover - depends on bot rights
        logger.warning('Не удалось удалить сообщение с суммой PayPalych', delete_error=delete_error)

    if prompt_message_id:
        try:
            await message.bot.delete_message(prompt_chat_id, prompt_message_id)
        except Exception as delete_error:  # pragma: no cover - diagnostic
            logger.warning('Не удалось удалить сообщение с запросом суммы PayPalych', delete_error=delete_error)

    if len(available_methods) == 1:
        await _send_pal24_payment_message(
            message,
            db_user,
            db,
            amount_kopeks,
            available_methods[0],
            state,
        )
        return

    await state.update_data(pal24_amount_kopeks=amount_kopeks)
    await state.set_state(BalanceStates.waiting_for_pal24_method)

    method_buttons: list[list[types.InlineKeyboardButton]] = []
    if 'sbp' in available_methods:
        method_buttons.append(
            [
                types.InlineKeyboardButton(
                    text=settings.get_pal24_sbp_button_text(
                        texts.t('PAL24_SBP_PAY_BUTTON', '🏦 Оплатить через PayPalych (СБП)')
                    ),
                    callback_data='pal24_method_sbp',
                )
            ]
        )
    if 'card' in available_methods:
        method_buttons.append(
            [
                types.InlineKeyboardButton(
                    text=settings.get_pal24_card_button_text(
                        texts.t('PAL24_CARD_PAY_BUTTON', '💳 Оплатить банковской картой (PayPalych)')
                    ),
                    callback_data='pal24_method_card',
                )
            ]
        )

    method_buttons.append([types.InlineKeyboardButton(text=texts.BACK, callback_data='balance_topup')])

    await message.answer(
        texts.t(
            'PAL24_SELECT_PAYMENT_METHOD',
            'Выберите способ оплаты PayPalych:',
        ),
        reply_markup=types.InlineKeyboardMarkup(inline_keyboard=method_buttons),
    )


@error_handler
async def handle_pal24_method_selection(
    callback: types.CallbackQuery,
    db_user: User,
    state: FSMContext,
):
    data = await state.get_data()
    amount_kopeks = data.get('pal24_amount_kopeks')
    if not amount_kopeks:
        texts = get_texts(db_user.language)
        await callback.answer(
            texts.t(
                'PAL24_PAYMENT_ERROR',
                '❌ Ошибка создания платежа PayPalych. Попробуйте позже или обратитесь в поддержку.',
            ),
            show_alert=True,
        )
        await state.clear()
        return

    method = 'sbp' if callback.data.endswith('_sbp') else 'card'

    await callback.answer()

    async with AsyncSessionLocal() as db:
        await _send_pal24_payment_message(
            callback.message,
            db_user,
            db,
            int(amount_kopeks),
            method,
            state,
        )


@error_handler
async def check_pal24_payment_status(
    callback: types.CallbackQuery,
    db: AsyncSession,
):
    try:
        local_payment_id = int(callback.data.split('_')[-1])
        payment_service = PaymentService(callback.bot)
        status_info = await payment_service.get_pal24_payment_status(db, local_payment_id)

        if not status_info:
            await callback.answer('❌ Платеж не найден', show_alert=True)
            return

        payment = status_info['payment']

        status_labels = {
            'NEW': ('⏳', 'Ожидает оплаты'),
            'PROCESS': ('⌛', 'Обрабатывается'),
            'SUCCESS': ('✅', 'Оплачен'),
            'FAIL': ('❌', 'Отменен'),
            'UNDERPAID': ('⚠️', 'Недоплата'),
            'OVERPAID': ('⚠️', 'Переплата'),
        }

        emoji, status_text = status_labels.get(payment.status, ('❓', 'Неизвестно'))

        metadata = payment.metadata_json or {}
        links_meta = metadata.get('links') if isinstance(metadata, dict) else None
        if not isinstance(links_meta, dict):
            links_meta = {}

        links_info = status_info.get('links') or {}

        def _extract_link(source: Any, keys: tuple[str, ...]) -> str | None:
            stack: list[Any] = [source]
            while stack:
                current = stack.pop()
                if isinstance(current, dict):
                    for key in keys:
                        value = current.get(key)
                        if value:
                            return str(value)
                    stack.extend(current.values())
                elif isinstance(current, list):
                    stack.extend(current)
            return None

        raw_response = metadata.get('raw_response') if isinstance(metadata, dict) else None
        remote_data = status_info.get('remote_data')
        transfer_keys = (
            'transfer_url',
            'transferUrl',
            'transfer_link',
            'transferLink',
            'transfer',
            'sbp_url',
            'sbpUrl',
            'sbp_link',
            'sbpLink',
        )
        card_keys = (
            'link_url',
            'linkUrl',
            'link',
            'card_url',
            'cardUrl',
            'card_link',
            'cardLink',
            'payment_url',
            'paymentUrl',
            'url',
        )

        extra_sbp_link = _extract_link(raw_response, transfer_keys) if raw_response else None
        if not extra_sbp_link and remote_data:
            extra_sbp_link = _extract_link(remote_data, transfer_keys)

        extra_card_link = _extract_link(raw_response, card_keys) if raw_response else None
        if not extra_card_link and remote_data:
            extra_card_link = _extract_link(remote_data, card_keys)

        sbp_link = (
            links_info.get('sbp')
            or links_meta.get('sbp')
            or status_info.get('sbp_url')
            or extra_sbp_link
            or payment.link_url
        )
        card_link = links_info.get('card') or links_meta.get('card') or status_info.get('card_url') or extra_card_link

        if not card_link and payment.link_page_url and payment.link_page_url != sbp_link:
            card_link = payment.link_page_url

        message_lines = [
            '🏦 Статус платежа PayPalych:',
            '',
            f'🆔 ID счета: {payment.bill_id}',
            f'💰 Сумма: {settings.format_price(payment.amount_kopeks)}',
            f'📊 Статус: {emoji} {status_text}',
            f'📅 Создан: {payment.created_at.strftime("%d.%m.%Y %H:%M")}',
        ]

        if payment.is_paid:
            message_lines.append('')
            message_lines.append('✅ Платеж успешно завершен! Средства уже на балансе.')
        elif payment.status in {'NEW', 'PROCESS'}:
            message_lines.append('')
            message_lines.append('⏳ Платеж еще не завершен. Оплатите счет и проверьте статус позже.')
            if sbp_link:
                message_lines.append('')
                message_lines.append(f'🏦 СБП: {sbp_link}')
            if card_link and card_link != sbp_link:
                message_lines.append(f'💳 Банковская карта: {card_link}')
        elif payment.status in {'FAIL', 'UNDERPAID', 'OVERPAID'}:
            message_lines.append('')
            message_lines.append(
                f'❌ Платеж не завершен корректно. Обратитесь в {settings.get_support_contact_display()}'
            )

        from app.localization.texts import get_texts

        db_user = getattr(callback, 'db_user', None)
        texts = get_texts(db_user.language if db_user else 'ru') if db_user else get_texts('ru')

        pay_rows: list[list[types.InlineKeyboardButton]] = []

        if not payment.is_paid and payment.status in {'NEW', 'PROCESS'}:
            default_sbp_text = texts.t(
                'PAL24_SBP_PAY_BUTTON',
                '🏦 Оплатить через PayPalych (СБП)',
            )
            sbp_button_text = settings.get_pal24_sbp_button_text(default_sbp_text)

            if sbp_link and settings.is_pal24_sbp_button_visible():
                pay_rows.append(
                    [
                        types.InlineKeyboardButton(
                            text=sbp_button_text,
                            url=sbp_link,
                        )
                    ]
                )

            default_card_text = texts.t(
                'PAL24_CARD_PAY_BUTTON',
                '💳 Оплатить банковской картой (PayPalych)',
            )
            card_button_text = settings.get_pal24_card_button_text(default_card_text)

            if card_link and settings.is_pal24_card_button_visible():
                if not pay_rows or pay_rows[-1][0].url != card_link:
                    pay_rows.append(
                        [
                            types.InlineKeyboardButton(
                                text=card_button_text,
                                url=card_link,
                            )
                        ]
                    )

        keyboard_rows = pay_rows + [
            [
                types.InlineKeyboardButton(
                    text=texts.t('CHECK_STATUS_BUTTON', '📊 Проверить статус'),
                    callback_data=f'check_pal24_{local_payment_id}',
                )
            ],
            [types.InlineKeyboardButton(text=texts.BACK, callback_data='balance_topup')],
        ]

        keyboard = types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows)

        await callback.answer()
        try:
            await callback.message.edit_text(
                '\n'.join(message_lines),
                reply_markup=keyboard,
                disable_web_page_preview=True,
            )
        except TelegramBadRequest as error:
            if 'message is not modified' in str(error).lower():
                await callback.answer(texts.t('CHECK_STATUS_NO_CHANGES', 'Статус не изменился'))
            else:
                raise

    except Exception as e:
        logger.error('Ошибка проверки статуса PayPalych', error=e)
        await callback.answer('❌ Ошибка проверки статуса', show_alert=True)
