import asyncio
import logging
from datetime import datetime
from aiogram import Bot, Dispatcher, F, types
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, ReplyKeyboardMarkup, KeyboardButton
from config import BOT_TOKEN
from keyboard import get_main_menu_keyboard
from db import (init_db, save_user, get_user_balance, update_user_balance, 
                save_payment, is_invoice_processed, save_invoice_payment)  # ✅ Добавлены новые функции
from crypto import make_crypto_pay_request, get_exchange_rate
from handlers.payment import payment_handler, back_handler
from handlers.sim_card import router as sim_card_router
from handlers.qr_code import router as qr_code_router
from handlers.get_card import router as get_card_router
from handlers.zayavki import router as zayavki_router
from handlers.support import router as support_router
from handlers.my_card import router as my_card_router
from handlers.vivod import router as vivod_router
from handlers.calculator import router as calculator_router

# Настройка логирования
logging.basicConfig(level=logging.CRITICAL)
logger = logging.getLogger(__name__)

# Инициализация бота и диспетчера с FSM
storage = MemoryStorage()
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(storage=storage)

# Регистрация роутеров
dp.include_router(sim_card_router)
dp.include_router(qr_code_router)
dp.include_router(get_card_router)
dp.include_router(zayavki_router)
dp.include_router(support_router)
dp.include_router(my_card_router)
dp.include_router(vivod_router)
dp.include_router(calculator_router)

# FSM для ввода суммы
class Form(StatesGroup):
    amount = State()

@dp.message(Command("start"))
async def start_handler(message: Message):
    user = message.from_user
    user_id = user.id
    username = user.username or ""
    first_name = user.first_name or ""
    last_name = user.last_name or ""
 
    init_db()
    save_user(user_id, username, first_name, last_name)
    await message.answer("Добро пожаловать")
 
    keyboard = ReplyKeyboardMarkup(
        keyboard=[
            [KeyboardButton(text="💳 Оплата"), KeyboardButton(text="👤 Личный кабинет")],
            [KeyboardButton(text="🧮 Калькулятор")],
            [KeyboardButton(text="📋 Мои заявки"), KeyboardButton(text="❓ Тех поддержка")]
        ],
        resize_keyboard=True
    )
    await message.answer("Выберите действие:", reply_markup=keyboard)

@dp.message(F.text == "💳 Оплата")
async def register_payment(message: Message):
    await payment_handler(message)

@dp.message(F.text == "🔙 Назад")
async def register_back(message: Message):
    await back_handler(message)

@dp.message(F.text == "👤 Личный кабинет")
async def personal_cabinet_handler(message: Message):
    user_id = message.from_user.id
    balance = get_user_balance(user_id)
    text = f"Ваш ID: {user_id}\nВаш баланс = {balance:.2f} рублей"
 
    keyboard = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="💰 Пополнить баланс", callback_data="top_up_balance")],
        [InlineKeyboardButton(text="💸 Вывод средств", callback_data="withdraw_funds")],
        [InlineKeyboardButton(text="💳 Мои карты", callback_data="my_cards")],
        [InlineKeyboardButton(text="🔙 Назад", callback_data="back_to_menu")]
    ])
 
    await message.answer(text, reply_markup=keyboard)

@dp.callback_query(F.data == "top_up_balance")
async def top_up_balance_handler(callback: CallbackQuery, state: FSMContext):
    await callback.message.answer("Введите сумму для оплаты в рублях:")
    await state.set_state(Form.amount)
    await callback.answer()

@dp.message(Form.amount)
async def process_amount(message: Message, state: FSMContext):
    try:
        rub_amount = float(message.text)
        if rub_amount <= 0:
            await message.answer("Сумма должна быть положительной. Введите заново:")
            return
    except ValueError:
        await message.answer("Неверный формат. Введите число (рублей):")
        return
 
    user_id = message.from_user.id
    exchange_rate = get_exchange_rate("USDT", "RUB")
    usd_amount = rub_amount / exchange_rate
    usd_amount_str = f"{usd_amount:.2f}"
 
    params = {
        "amount": usd_amount_str,
        "asset": "USDT",
        "description": f"Пополнение баланса на {rub_amount} рублей для пользователя {user_id}",
        "payload": str(user_id),
        "expires_in": 3600
    }
 
    result = make_crypto_pay_request("createInvoice", params)
 
    if result.get("ok"):
        invoice = result["result"]
        invoice_url = invoice["bot_invoice_url"]
        invoice_id = invoice["invoice_id"]
     
        text = f"✅ Инвойс создан на {rub_amount} рублей (~{usd_amount_str} USD)."
     
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="💳 Оплатить", url=invoice_url)],
            [InlineKeyboardButton(text="🔍 Проверить оплату", callback_data=f"check_{invoice_id}")]
        ])
     
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML")
        await state.update_data(
            invoice_id=invoice_id,
            rub_amount=rub_amount,
            exchange_rate=exchange_rate
        )
    else:
        error = result.get("error", {}).get("description", "Неизвестная ошибка")
        await message.answer(f"❌ Ошибка создания инвойса: {error}")
 
    await state.clear()

@dp.message(Command("check_balance"))
async def check_balance_command(message: Message):
    """Обработчик команды /check_balance (для общих проверок)"""
    user_id = message.from_user.id
    params = {
        "status": "paid",
        "count": 5,
        "offset": 0
    }
    result = make_crypto_pay_request("getInvoices", params)
 
    if result.get("ok"):
        result_data = result.get("result", {})
        invoices = result_data.get("items", []) if isinstance(result_data, dict) else result_data
        for invoice in invoices:
            invoice_id = invoice.get("invoice_id")
            
            # ✅ Проверяем через БД, не был ли инвойс уже обработан
            if is_invoice_processed(invoice_id):
                continue
            
            if invoice.get("payload") == str(user_id) and invoice.get("status") == "paid":
                if "paid_amount" in invoice:
                    usd_paid = float(invoice["paid_amount"])
                else:
                    usd_paid = float(invoice["amount"])
             
                exchange_rate = get_exchange_rate("USDT", "RUB")
                rub_amount = usd_paid * exchange_rate
                
                # Обновляем баланс
                update_user_balance(user_id, rub_amount)
                
                # ✅ Сохраняем в БД как обработанный
                save_invoice_payment(invoice_id, user_id, rub_amount, usd_paid)
             
                balance = get_user_balance(user_id)
                await message.answer(
                    f"✅ Оплата подтверждена!\n"
                    f"Баланс пополнен на {rub_amount:.2f} рублей.\n"
                    f"Новый баланс: {balance:.2f} рублей"
                )
                return
        await message.answer("❌ Оплата не найдена или не завершена.")
    else:
        error = result.get("error", {}).get("description", "Неизвестная ошибка API")
        await message.answer(f"❌ Ошибка при проверке: {error}")

@dp.callback_query(F.data.startswith("check_"))
async def check_balance_callback(callback: CallbackQuery, state: FSMContext):
    """Обработчик кнопки 'Проверить оплату'"""
    user_id = callback.from_user.id
 
    try:
        invoice_id_str = callback.data.split("_", 1)[1]
        invoice_id = int(invoice_id_str)
        
        # ✅ ПРОВЕРКА: был ли инвойс уже обработан (через БД)
        if is_invoice_processed(invoice_id):
            await callback.message.answer("✅ Этот инвойс уже был обработан ранее.")
            await callback.answer()
            return
     
        params = {"invoice_ids": str(invoice_id)}
        result = make_crypto_pay_request("getInvoices", params)
     
        if not result.get("ok"):
            error_msg = result.get("error", {}).get("description", "Неизвестная ошибка API")
            logger.error(f"API Error: {error_msg}")
            await callback.message.answer(f"❌ Ошибка API: {error_msg}")
            await callback.answer()
            return
     
        result_data = result.get("result", {})
        invoices = result_data.get("items", []) if isinstance(result_data, dict) else result_data
     
        if not invoices:
            await callback.message.answer("❌ Инвойс не найден.")
            await callback.answer()
            return
     
        invoice = invoices[0]
        status = invoice.get("status", "unknown")
        payload = invoice.get("payload", "")
     
        if payload != str(user_id):
            await callback.message.answer("❌ Этот инвойс не для вас.")
            await callback.answer()
            return
     
        if status == "paid":
            try:
                data = await state.get_data()
                exchange_rate = data.get("exchange_rate")
                original_rub_amount = data.get("rub_amount")
              
                if exchange_rate is None:
                    exchange_rate = get_exchange_rate("USDT", "RUB")
              
                if "paid_amount" in invoice:
                    amount = float(invoice["paid_amount"])
                else:
                    amount = float(invoice["amount"])
             
                rub_amount = amount * exchange_rate
                
                # Обновляем баланс
                update_user_balance(user_id, rub_amount)
                
                # ✅ Сохраняем в БД как обработанный
                if save_invoice_payment(invoice_id, user_id, rub_amount, amount):
                    balance = get_user_balance(user_id)
                    await callback.message.answer(
                        f"✅ Оплата подтверждена!\n"
                        f"Баланс пополнен на {rub_amount:.2f} рублей.\n"
                        f"Новый баланс: {balance:.2f} рублей"
                    )
                else:
                    # Если save_invoice_payment вернул False, значит инвойс уже был обработан
                    await callback.message.answer("✅ Этот инвойс уже был обработан ранее.")
             
            except (ValueError, KeyError) as e:
                logger.error(f"Error processing payment: {e}")
                await callback.message.answer("❌ Ошибка при обработке платежа.")
     
        elif status == "expired":
            await callback.message.answer("❌ Инвойс истек. Создайте новый.")
        elif status == "active":
            await callback.message.answer("⏳ Инвойс еще не оплачен. Ожидайте платежа...")
        else:
            await callback.message.answer(f"❌ Неизвестный статус: {status}")
     
        await callback.answer()
     
    except ValueError as e:
        logger.error(f"ValueError: {e}", exc_info=True)
        await callback.message.answer("❌ Ошибка при обработке ID инвойса.")
        await callback.answer()
    except Exception as e:
        logger.error(f"Unexpected error: {e}", exc_info=True)
        await callback.message.answer(f"❌ Неожиданная ошибка: {str(e)}")
        await callback.answer()

@dp.callback_query(F.data == "back_to_menu")
async def back_to_menu_handler(callback: CallbackQuery):
    keyboard = ReplyKeyboardMarkup(
        keyboard=[
            [KeyboardButton(text="💳 Оплата"), KeyboardButton(text="👤 Личный кабинет")],
            [KeyboardButton(text="🧮 Калькулятор")],
            [KeyboardButton(text="📋 Мои заявки"), KeyboardButton(text="❓ Тех поддержка")]
        ],
        resize_keyboard=True
    )
    await callback.message.answer("Главное меню:", reply_markup=keyboard)
    await callback.answer()

async def main():
    print("Бот запущен")
    try:
        await dp.start_polling(bot)
    except KeyboardInterrupt:
        print("Бот остановлен пользователем")
    except Exception as e:
        print(f"Бот отключен: {e}")
    finally:
        print("Бот остановлен")

if __name__ == "__main__":
    asyncio.run(main())