from __future__ import annotations

import json
import secrets
import time
from decimal import Decimal, ROUND_DOWN

from aiogram import Router, F
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, Message, WebAppInfo
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup

from config import load_config
from utils.payment_models import create_mulenpay_invoice, create_heleket_invoice
from utils.cryptopay import create_fiat_invoice_rub, get_invoice_by_id
from utils.toncenter import find_incoming_by_comment

router = Router()
config = load_config()


def profile_menu_kb(user_id):
    return InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="💰 Пополнить баланс", callback_data="profile_topup")],
        [InlineKeyboardButton(text="🎟️ Промокоды", callback_data="profile_activate_promo")],
        [InlineKeyboardButton(text="👥 Реферальная система", callback_data="profile_referral")],
        [InlineKeyboardButton(text="📄 Публичная оферта", url=config.profile_offer_url)],
        [InlineKeyboardButton(text="🔒 Политика конфиденциальности", url=config.profile_privacy_url)],
        [InlineKeyboardButton(text="⬅️ Назад", callback_data="main_menu")],
    ])


async def get_user_by_id(db_pool, user_id):
    async with db_pool.acquire() as conn:
        return await conn.fetchrow("SELECT * FROM users WHERE telegram_id=$1", user_id)


async def get_payment_settings(db_pool, system: str):
    async with db_pool.acquire() as conn:
        return await conn.fetchrow("SELECT * FROM payment_settings WHERE system=$1", system)


async def add_payment(db_pool, uuid: str, user_id: int, amount: Decimal):
    async with db_pool.acquire() as conn:
        await conn.execute(
            "INSERT INTO payments (uuid, user_id, amount, is_paid) VALUES ($1, $2, $3, false)",
            uuid,
            user_id,
            amount,
        )


async def mark_payment_paid_and_credit(db_pool, uuid: str) -> Decimal | None:
    """Atomically mark payment paid and credit user balance. Returns amount or None if already processed."""
    async with db_pool.acquire() as conn:
        row = await conn.fetchrow(
            "UPDATE payments SET is_paid=true WHERE uuid=$1 AND is_paid=false RETURNING user_id, amount",
            uuid,
        )
        if not row:
            return None
        amount = Decimal(str(row["amount"]))
        user_id = int(row["user_id"])
        await conn.execute("UPDATE users SET balance = balance + $1 WHERE telegram_id=$2", float(amount), user_id)
        return amount


# ===== Profile =====
@router.callback_query(F.data == "profile")
async def profile_callback(callback: CallbackQuery):
    db_pool = callback.bot.get("db_pool")
    user = await get_user_by_id(db_pool, callback.from_user.id)
    if not user:
        await callback.answer("Пользователь не найден")
        return

    text = (
        "<b>👤 Ваш профиль</b>\n\n"
        f"💰 Баланс: <b>{user['balance']} RUB</b>\n"
        f"🎟️ Скидка: <b>{user['discount']}%</b>\n"
        f"👥 Рефералы: <b>{user['referrals']} чел.</b>\n"
    )

    await callback.message.edit_caption(caption=text, reply_markup=profile_menu_kb(callback.from_user.id), parse_mode="HTML")
    await callback.answer()


# ===== Topup menu =====
@router.callback_query(F.data == "profile_topup")
async def profile_topup_callback(callback: CallbackQuery):
    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Банковская карта (СБП)", callback_data="topup_sbp")],
        [InlineKeyboardButton(text="CryptoBot (Crypto Pay)", callback_data="topup_cryptobot")],
        [InlineKeyboardButton(text="TON (TonConnect)", callback_data="topup_tonconnect")],
        [InlineKeyboardButton(text="Назад", callback_data="profile")],
    ])

    await callback.message.edit_caption(
        caption="<b>💰 Пополнение баланса</b>\n\nВыберите способ пополнения:",
        reply_markup=kb,
        parse_mode="HTML",
    )
    await callback.answer()


# ===== SBP (existing) =====
class TopupSBPStates(StatesGroup):
    waiting_for_amount = State()


@router.callback_query(F.data == "topup_sbp")
async def topup_sbp_callback(callback: CallbackQuery, state: FSMContext):
    db_pool = callback.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "sbp")
    min_amount = float(settings["min_amount"]) if settings else 10

    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")]])

    await callback.message.answer(
        f"Введите сумму пополнения (минимум {min_amount} RUB):",
        reply_markup=kb,
    )
    await state.set_state(TopupSBPStates.waiting_for_amount)
    await callback.answer()


@router.message(TopupSBPStates.waiting_for_amount)
async def process_topup_sbp_amount(message: Message, state: FSMContext):
    db_pool = message.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "sbp")
    min_amount = float(settings["min_amount"]) if settings else 10

    try:
        amount = float(message.text.replace(",", "."))
        if amount < min_amount:
            raise ValueError
    except Exception:
        await message.answer(f"❌ Введите корректную сумму (минимум {min_amount} RUB).")
        return

    unique_uuid = f"sbp_{message.from_user.id}_{int(time.time())}"

    # Создаем инвойс у MulenPay
    invoice = create_mulenpay_invoice(unique_uuid, amount)
    await add_payment(db_pool, unique_uuid, message.from_user.id, Decimal(str(amount)))

    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Оплатить", url=invoice["paymentUrl"])],
        [InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")],
    ])

    await message.answer(
        f"✅ Счет создан!\n\nСумма: <b>{amount} RUB</b>\n\nНажмите кнопку ниже для оплаты:",
        reply_markup=kb,
        parse_mode="HTML",
    )
    await state.clear()


# ===== CryptoBot (Crypto Pay API) =====
class TopupCryptoBotStates(StatesGroup):
    waiting_for_amount = State()


@router.callback_query(F.data == "topup_cryptobot")
async def topup_cryptobot_callback(callback: CallbackQuery, state: FSMContext):
    db_pool = callback.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "cryptobot")
    min_amount = float(settings["min_amount"]) if settings else 50

    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")]])

    await callback.message.answer(
        f"Введите сумму пополнения через CryptoBot (минимум {min_amount} RUB):",
        reply_markup=kb,
    )
    await state.set_state(TopupCryptoBotStates.waiting_for_amount)
    await callback.answer()


@router.message(TopupCryptoBotStates.waiting_for_amount)
async def process_cryptobot_amount(message: Message, state: FSMContext):
    db_pool = message.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "cryptobot")
    min_amount = float(settings["min_amount"]) if settings else 50

    try:
        amount = float(message.text.replace(",", "."))
        if amount < min_amount:
            raise ValueError
    except Exception:
        await message.answer(f"❌ Введите корректную сумму (минимум {min_amount} RUB).")
        return

    # Crypto Pay API invoice
    if not config.cryptopay_token:
        await message.answer("❌ CRYPTOPAY_TOKEN не задан в .env")
        await state.clear()
        return

    invoice = create_fiat_invoice_rub(
        token=config.cryptopay_token,
        amount_rub=amount,
        description="Пополнение баланса",
        payload=str(message.from_user.id),
        paid_btn_name="viewItem",
        paid_btn_url=config.news_channel_link or None,
    )

    invoice_id = int(invoice["invoice_id"])
    payment_uuid = f"cryptobot_{invoice_id}"

    await add_payment(db_pool, payment_uuid, message.from_user.id, Decimal(str(amount)))

    pay_url = invoice.get("bot_invoice_url") or invoice.get("web_app_invoice_url")

    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Оплатить в CryptoBot", url=pay_url)],
        [InlineKeyboardButton(text="Проверить оплату", callback_data=f"check_cryptobot_{invoice_id}")],
        [InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")],
    ])

    await message.answer(
        f"✅ Счет создан!\n\nСумма: <b>{amount} RUB</b>\n\nОплатите и нажмите «Проверить оплату».",
        reply_markup=kb,
        parse_mode="HTML",
    )
    await state.clear()


@router.callback_query(F.data.startswith("check_cryptobot_"))
async def check_cryptobot(callback: CallbackQuery):
    db_pool = callback.bot.get("db_pool")
    try:
        invoice_id = int(callback.data.split("_", 2)[2])
    except Exception:
        await callback.answer("Ошибка")
        return

    if not config.cryptopay_token:
        await callback.answer("CRYPTOPAY_TOKEN не задан")
        return

    inv = get_invoice_by_id(config.cryptopay_token, invoice_id)
    if not inv:
        await callback.answer("Инвойс не найден")
        return

    status = inv.get("status")
    if status != "paid":
        await callback.answer("Платёж ещё не оплачен")
        return

    payment_uuid = f"cryptobot_{invoice_id}"
    amount = await mark_payment_paid_and_credit(db_pool, payment_uuid)
    if amount is None:
        await callback.answer("Уже обработано")
        return

    await callback.message.answer(f"✅ Баланс пополнен на {amount} RUB!")
    await callback.answer()


# ===== TON (TonConnect) =====
class TopupTonConnectStates(StatesGroup):
    waiting_for_amount_rub = State()


@router.callback_query(F.data == "topup_tonconnect")
async def topup_tonconnect_callback(callback: CallbackQuery, state: FSMContext):
    db_pool = callback.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "tonconnect")
    min_amount = float(settings["min_amount"]) if settings else 50
    rate = Decimal(str(settings["exchange_rate"])) if settings and settings["exchange_rate"] is not None else None

    if not config.public_base_url or not config.ton_receiver_address:
        await callback.message.answer(
            "❌ Для TonConnect нужно задать в .env: PUBLIC_BASE_URL и TON_RECEIVER_ADDRESS"
        )
        await callback.answer()
        return

    if rate is None or rate <= 0:
        await callback.message.answer(
            "❌ Для TonConnect нужно задать payment_settings.exchange_rate (RUB за 1 TON).\n"
            "Смотри database/patch_2026_cryptobot_tonconnect.sql"
        )
        await callback.answer()
        return

    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")]])

    await callback.message.answer(
        f"Введите сумму пополнения в RUB (минимум {min_amount} RUB).\n"
        f"Текущий курс: 1 TON = {rate} RUB",
        reply_markup=kb,
    )
    await state.set_state(TopupTonConnectStates.waiting_for_amount_rub)
    await callback.answer()


@router.message(TopupTonConnectStates.waiting_for_amount_rub)
async def process_tonconnect_amount(message: Message, state: FSMContext):
    db_pool = message.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "tonconnect")
    min_amount = float(settings["min_amount"]) if settings else 50
    rate = Decimal(str(settings["exchange_rate"])) if settings and settings["exchange_rate"] is not None else None

    try:
        amount_rub = Decimal(message.text.replace(",", ".")).quantize(Decimal("0.01"))
        if float(amount_rub) < min_amount:
            raise ValueError
    except Exception:
        await message.answer(f"❌ Введите корректную сумму (минимум {min_amount} RUB).")
        return

    if not rate or rate <= 0:
        await message.answer("❌ Не задан exchange_rate для TonConnect")
        await state.clear()
        return

    # amount in TON
    ton_amount = (amount_rub / rate).quantize(Decimal("0.000000001"), rounding=ROUND_DOWN)

    payment_uuid = f"ton_{secrets.token_hex(12)}"  # fits in 64 chars
    comment = f"PAY:{payment_uuid}"

    # store to DB
    await add_payment(db_pool, payment_uuid, message.from_user.id, amount_rub)
    async with db_pool.acquire() as conn:
        await conn.execute(
            "INSERT INTO tonconnect_payments (uuid, ton_amount, comment) VALUES ($1, $2, $3)",
            payment_uuid,
            ton_amount,
            comment,
        )

    webapp_url = f"{config.public_base_url}/static/tonconnect/deposit.html?payment={payment_uuid}"

    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Открыть TonConnect", web_app=WebAppInfo(url=webapp_url))],
        [InlineKeyboardButton(text="Проверить оплату", callback_data=f"check_ton_{payment_uuid}")],
        [InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")],
    ])

    await message.answer(
        "✅ Счет создан!\n\n"
        f"Сумма: <b>{amount_rub} RUB</b>\n"
        f"К оплате: <b>{ton_amount} TON</b>\n\n"
        "Нажмите «Открыть TonConnect» и отправьте перевод. Затем вернитесь и нажмите «Проверить оплату».",
        reply_markup=kb,
        parse_mode="HTML",
    )

    await state.clear()


@router.callback_query(F.data.startswith("check_ton_"))
async def check_tonconnect(callback: CallbackQuery):
    db_pool = callback.bot.get("db_pool")
    payment_uuid = callback.data.replace("check_ton_", "", 1)

    # load expected ton_amount + comment
    async with db_pool.acquire() as conn:
        meta = await conn.fetchrow("SELECT * FROM tonconnect_payments WHERE uuid=$1", payment_uuid)
        pay = await conn.fetchrow("SELECT * FROM payments WHERE uuid=$1", payment_uuid)

    if not meta or not pay:
        await callback.answer("Платёж не найден")
        return

    if pay["is_paid"]:
        await callback.answer("Уже обработано")
        return

    if not config.ton_receiver_address:
        await callback.answer("TON_RECEIVER_ADDRESS не задан")
        return

    ton_amount = Decimal(str(meta["ton_amount"]))
    comment = str(meta["comment"]) or f"PAY:{payment_uuid}"

    # nanotons minimal (tolerance: -0.5%)
    min_value_nano = int((ton_amount * Decimal(1_000_000_000) * Decimal("0.995")).to_integral_value(rounding=ROUND_DOWN))

    try:
        incoming = find_incoming_by_comment(
            base_url=config.toncenter_base_url,
            receiver_address=config.ton_receiver_address,
            comment=comment,
            min_value_nano=min_value_nano,
            api_key=config.toncenter_api_key,
            limit=50,
        )
    except Exception:
        incoming = None

    if not incoming:
        await callback.answer("Не вижу оплату в сети. Подождите и попробуйте ещё раз.")
        return

    # mark paid
    amount = await mark_payment_paid_and_credit(db_pool, payment_uuid)
    if amount is None:
        await callback.answer("Уже обработано")
        return

    async with db_pool.acquire() as conn:
        await conn.execute(
            "UPDATE tonconnect_payments SET tx_hash=$1, paid_at=now() WHERE uuid=$2",
            incoming.tx_hash,
            payment_uuid,
        )

    await callback.message.answer(f"✅ Баланс пополнен на {amount} RUB!")
    await callback.answer()


# ===== Old crypto provider (Heleket) kept for compatibility =====
class TopupHeleketStates(StatesGroup):
    waiting_for_amount = State()


@router.callback_query(F.data == "topup_crypto")
async def topup_crypto_callback(callback: CallbackQuery, state: FSMContext):
    """Legacy: Heleket invoices."""
    db_pool = callback.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "crypto")
    min_amount = float(settings["min_amount"]) if settings else 75

    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")]])

    await callback.message.answer(
        f"Введите сумму пополнения (минимум {min_amount} RUB):",
        reply_markup=kb,
    )
    await state.set_state(TopupHeleketStates.waiting_for_amount)
    await callback.answer()


@router.message(TopupHeleketStates.waiting_for_amount)
async def process_topup_crypto_amount(message: Message, state: FSMContext):
    db_pool = message.bot.get("db_pool")
    settings = await get_payment_settings(db_pool, "crypto")
    min_amount = float(settings["min_amount"]) if settings else 75

    try:
        amount = float(message.text.replace(",", "."))
        if amount < min_amount:
            raise ValueError
    except Exception:
        await message.answer(f"❌ Введите корректную сумму (минимум {min_amount} RUB).")
        return

    unique_uuid = f"crypto_{message.from_user.id}_{int(time.time())}"

    invoice = create_heleket_invoice(unique_uuid, amount)
    await add_payment(db_pool, unique_uuid, message.from_user.id, Decimal(str(amount)))

    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Оплатить", url=invoice["payment_url"] if "payment_url" in invoice else invoice.get("url"))],
        [InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_topup")],
    ])

    await message.answer(
        f"✅ Счет создан!\n\nСумма: <b>{amount} RUB</b>\n\nНажмите кнопку ниже для оплаты:",
        reply_markup=kb,
        parse_mode="HTML",
    )
    await state.clear()


# ===== Cancel =====
@router.callback_query(F.data == "cancel_topup")
async def cancel_topup(callback: CallbackQuery, state: FSMContext):
    await state.clear()
    await callback.message.answer("❌ Пополнение отменено.")
    await callback.answer()


# ===== Promo / referral parts (unchanged) =====


async def get_ref_stats(db_pool, inviter_id: int):
    async with db_pool.acquire() as conn:
        referrals = await conn.fetch("SELECT telegram_id FROM users WHERE invited_by=$1", inviter_id)
        count = len(referrals)
        total_earnings = Decimal("0")
        for ref in referrals:
            payments = await conn.fetch(
                "SELECT amount FROM payments WHERE user_id=$1 AND is_paid=true",
                ref["telegram_id"],
            )
            for payment in payments:
                total_earnings += Decimal(str(payment["amount"])) * Decimal("0.05")
        return count, total_earnings


@router.callback_query(F.data == "profile_referral")
async def profile_referral(callback: CallbackQuery):
    db_pool = callback.bot.get("db_pool")
    inviter_id = callback.from_user.id
    count, earnings = await get_ref_stats(db_pool, inviter_id)

    text = (
        "<b>👥 Реферальная система</b>\n\n"
        f"Ваши рефералы: <b>{count}</b>\n"
        f"Ваш заработок: <b>{earnings:.2f} RUB</b> (5% от пополнений)\n\n"
        "Приглашайте друзей и получайте бонусы!\n"
        f"Ваша ссылка: https://t.me/{callback.bot.username}?start={inviter_id}"
    )

    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="⬅️ Назад", callback_data="profile")]])
    await callback.message.edit_caption(caption=text, reply_markup=kb, parse_mode="HTML")
    await callback.answer()


# Promo code models from old file
class ActivatePromoStates(StatesGroup):
    waiting_for_promo = State()


@router.callback_query(F.data == "profile_activate_promo")
async def profile_activate_promo(callback: CallbackQuery, state: FSMContext):
    kb = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_promo")]])
    await callback.message.answer("Введите промокод:", reply_markup=kb)
    await state.set_state(ActivatePromoStates.waiting_for_promo)
    await callback.answer()


@router.message(ActivatePromoStates.waiting_for_promo)
async def process_promo(message: Message, state: FSMContext):
    db_pool = message.bot.get("db_pool")
    promo = message.text.strip()

    async with db_pool.acquire() as conn:
        promo_row = await conn.fetchrow(
            "SELECT * FROM promo_codes WHERE code=$1 AND is_active=true AND expires_at > now()",
            promo,
        )
        if not promo_row:
            await message.answer("❌ Промокод недействителен или истек.")
            return
        used = await conn.fetchrow(
            "SELECT * FROM promo_history WHERE user_id=$1 AND promo_code_id=$2",
            message.from_user.id,
            promo_row["id"],
        )
        if used:
            await message.answer("❌ Вы уже использовали этот промокод.")
            return

        # Apply discount
        discount = int(promo_row["discount"])
        await conn.execute("UPDATE users SET discount=$1 WHERE telegram_id=$2", discount, message.from_user.id)
        await conn.execute(
            "INSERT INTO promo_history (user_id, promo_code_id, used_at) VALUES ($1, $2, now())",
            message.from_user.id,
            promo_row["id"],
        )

    await message.answer(f"✅ Промокод активирован! Ваша скидка: {discount}%")
    await state.clear()


@router.callback_query(F.data == "cancel_promo")
async def cancel_promo(callback: CallbackQuery, state: FSMContext):
    await state.clear()
    await callback.message.answer("❌ Отмена.")
    await callback.answer()
