#!/usr/bin/env python3
"""
Payment Handlers for Telegram Bot.

Handles:
- /buy command with package selection
- Invoice generation via YooKassa
- Pre-checkout validation
- Successful payment processing

Pricing:
- Regular: 699₽ / 2499₽ / 3999₽ (1/5/10 generations)
- Promo: 99₽ / 499₽ / 999₽ (one-time for first purchase only)
"""

import logging
import os
import time
from datetime import datetime
from typing import Optional

from telegram import Update, LabeledPrice, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
    ContextTypes,
    CommandHandler,
    CallbackQueryHandler,
    PreCheckoutQueryHandler,
    MessageHandler,
    filters,
)

from database import init_db, get_or_create_user, get_user, set_user_promo_eligible, get_accepted_terms_version, get_promo_remaining, set_promo_remaining, get_user_active_packs
from token_manager import (
    get_balance,
    is_promo_eligible,
    credit_tokens,
    get_user_info,
    check_and_grant_giveaway,
)

# Terms version - must match telegram_bot.py
# This is checked before allowing purchase
TERMS_VERSION = 1

logger = logging.getLogger(__name__)


def _plural_gen(n: int) -> str:
    """Russian plural for 'генерация': 1 генерация, 2-4 генерации, 5+ генераций."""
    if n % 10 == 1 and n % 100 != 11:
        return "генерация"
    elif n % 10 in (2, 3, 4) and n % 100 not in (12, 13, 14):
        return "генерации"
    return "генераций"

# =============================================================================
# Configuration
# =============================================================================

# YooKassa provider token from BotFather
PAYMENT_PROVIDER_TOKEN = os.environ.get("PAYMENT_PROVIDER_TOKEN", "")

# Token packages with pricing (in kopecks, 100 kopecks = 1 RUB)
# Regular: 3 gen = 500₽, 10 gen = 1500₽, 20 gen = 2000₽
# Promo:   3 gen = 250₽, 10 gen = 750₽,  20 gen = 1500₽
PACKAGES = {
    "3":  {"tokens": 3,  "regular": 50000,  "promo": 25000},   # 500₽ / 250₽
    "10": {"tokens": 10, "regular": 150000, "promo": 75000},   # 1500₽ / 750₽
    "20": {"tokens": 20, "regular": 200000, "promo": 150000},  # 2000₽ / 1500₽
}


def format_price(kopecks: int) -> str:
    """Format price in kopecks as RUB string."""
    rub = kopecks / 100
    if rub == int(rub):
        return f"{int(rub)}₽"
    return f"{rub:.2f}₽"


# =============================================================================
# Handlers
# =============================================================================

async def buy_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Handle /buy command - show token packages.
    """
    user = update.effective_user
    user_id = user.id

    # Track if user is new
    existing_user = get_user(user_id)
    is_new = existing_user is None

    # Ensure user exists in DB
    get_or_create_user(
        user_id,
        username=user.username,
        first_name=user.first_name,
        last_name=user.last_name
    )

    # Check and grant giveaway tokens silently
    check_and_grant_giveaway(user_id, is_new)

    # Check if user is promo-eligible AND promo is currently available
    promo = is_promo_eligible(user_id) and get_promo_remaining() > 0
    current_balance = get_balance(user_id)

    # Build message
    if promo:
        text = "🎁 *Специальное предложение для первой покупки!*\n\n"
        text += "⚡ Сниженные цены действуют только один раз.\n\n"
    else:
        text = "💳 *Покупка генераций*\n\n"
        text += "1 генерация = 1 видео\n\n"

    # Build keyboard with packages
    keyboard = []
    for pkg_id, pkg in PACKAGES.items():
        price = pkg["promo"] if promo else pkg["regular"]
        tokens = pkg["tokens"]

        # Calculate per-generation price
        per_gen = price / tokens / 100

        label = f"{tokens} {_plural_gen(tokens)} — {format_price(price)}"
        if tokens > 1:
            label += f" ({per_gen:.0f}₽/шт)"

        keyboard.append([
            InlineKeyboardButton(label, callback_data=f"buy:{pkg_id}")
        ])

    # Add cancel button
    keyboard.append([
        InlineKeyboardButton("❌ Отмена", callback_data="buy:cancel")
    ])

    text += f"\n💰 Ваш баланс: *{current_balance}* {_plural_gen(current_balance)}"

    await update.effective_message.reply_text(
        text,
        reply_markup=InlineKeyboardMarkup(keyboard),
        parse_mode="Markdown"
    )


async def handle_buy_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Handle package selection callback - send invoice.
    """
    query = update.callback_query
    await query.answer()

    user = update.effective_user
    user_id = user.id

    # Parse callback data
    _, pkg_id = query.data.split(":", 1)

    if pkg_id == "cancel":
        await query.edit_message_text("❌ Покупка отменена")
        return

    if pkg_id not in PACKAGES:
        await query.edit_message_text("❌ Неверный пакет")
        return

    pkg = PACKAGES[pkg_id]
    tokens = pkg["tokens"]

    # Get price based on promo eligibility AND availability
    promo = is_promo_eligible(user_id) and get_promo_remaining() > 0
    price = pkg["promo"] if promo else pkg["regular"]

    # Create invoice
    title = f"{tokens} {_plural_gen(tokens)} видео"
    description = (
        f"Пакет из {tokens} {_plural_gen(tokens)} промо-видео товаров Wildberries. "
        f"1 генерация = 1 видео."
    )

    if promo:
        description += "\n\n🎁 Применена специальная скидка!"

    # Unique payload for this purchase
    payload = f"tokens_{tokens}_{user_id}_{promo}"

    prices = [LabeledPrice(f"{tokens} {_plural_gen(tokens)}", price)]

    try:
        # Delete the selection message
        await query.delete_message()

        # Send invoice
        await context.bot.send_invoice(
            chat_id=user_id,
            title=title,
            description=description,
            payload=payload,
            provider_token=PAYMENT_PROVIDER_TOKEN,
            currency="RUB",
            prices=prices,
            start_parameter=f"buy_{tokens}",
            # Optional: photo
            # photo_url="https://...",
            # photo_size=512,
            # photo_width=512,
            # photo_height=512,
        )

        logger.info(f"Invoice sent to user {user_id}: {tokens} tokens for {price} kopecks")

    except Exception as e:
        logger.error(f"Failed to send invoice to {user_id}: {e}")
        await context.bot.send_message(
            chat_id=user_id,
            text=f"❌ Ошибка при создании счёта: {e}"
        )


async def precheckout_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Handle pre-checkout query - validate before payment.

    Must respond within 10 seconds!
    """
    # CRITICAL: Capture arrival time immediately
    arrival_time = time.time()
    arrival_dt = datetime.now().strftime("%H:%M:%S.%f")[:-3]

    query = update.pre_checkout_query
    user_id = query.from_user.id

    logger.info(
        f"[PRECHECKOUT] RECEIVED at {arrival_dt} from user {user_id}, "
        f"query_id={query.id}, amount={query.total_amount}"
    )

    # Parse payload
    parts = query.invoice_payload.split("_")
    if len(parts) < 3 or parts[0] != "tokens":
        elapsed = (time.time() - arrival_time) * 1000
        logger.warning(f"[PRECHECKOUT] REJECTED (bad payload) in {elapsed:.0f}ms")
        await query.answer(ok=False, error_message="Неверный формат платежа")
        return

    tokens = int(parts[1])
    payload_user_id = int(parts[2])

    # Validate user matches
    if user_id != payload_user_id:
        elapsed = (time.time() - arrival_time) * 1000
        logger.warning(f"[PRECHECKOUT] REJECTED (user mismatch) in {elapsed:.0f}ms")
        await query.answer(ok=False, error_message="Ошибка авторизации")
        return

    # Validate tokens (must match PACKAGES keys)
    if str(tokens) not in PACKAGES:
        elapsed = (time.time() - arrival_time) * 1000
        logger.warning(f"[PRECHECKOUT] REJECTED (bad tokens={tokens}) in {elapsed:.0f}ms")
        await query.answer(ok=False, error_message="Неверное количество генераций")
        return

    # All good - respond IMMEDIATELY
    pre_answer_time = time.time()
    await query.answer(ok=True)
    post_answer_time = time.time()

    total_elapsed = (post_answer_time - arrival_time) * 1000
    answer_elapsed = (post_answer_time - pre_answer_time) * 1000

    logger.info(
        f"[PRECHECKOUT] APPROVED for user {user_id}: {tokens} tokens | "
        f"Total: {total_elapsed:.0f}ms, answer() call: {answer_elapsed:.0f}ms"
    )


async def successful_payment_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Handle successful payment - credit tokens to user.
    """
    arrival_dt = datetime.now().strftime("%H:%M:%S.%f")[:-3]
    logger.info(f"[PAYMENT] SUCCESS callback received at {arrival_dt}")

    payment = update.message.successful_payment
    user = update.effective_user
    user_id = user.id

    # Parse payload
    parts = payment.invoice_payload.split("_")
    tokens = int(parts[1])
    promo = parts[3] == "True" if len(parts) > 3 else False

    # Get payment IDs
    telegram_payment_id = payment.telegram_payment_charge_id
    provider_payment_id = payment.provider_payment_charge_id
    total_amount = payment.total_amount  # in kopecks

    logger.info(
        f"Payment received from user {user_id}: "
        f"{tokens} tokens, {total_amount} kopecks, "
        f"telegram_id={telegram_payment_id}, provider_id={provider_payment_id}"
    )

    # Ensure user exists
    get_or_create_user(
        user_id,
        username=user.username,
        first_name=user.first_name,
        last_name=user.last_name
    )

    # Credit tokens
    description = f"Покупка {tokens} {_plural_gen(tokens)}"
    if promo:
        description += " (промо-цена)"

    success = credit_tokens(
        user_id=user_id,
        amount=tokens,
        txn_type="purchase",
        description=description,
        telegram_payment_charge_id=telegram_payment_id,
        provider_payment_charge_id=provider_payment_id,
        amount_rub=total_amount,
    )

    if success:
        new_balance = get_balance(user_id)

        # Remove promo eligibility and decrement counter after promo purchase
        if promo:
            set_user_promo_eligible(user_id, eligible=False)
            # Decrement promo users remaining counter
            promo_remaining = get_promo_remaining()
            if promo_remaining > 0:
                set_promo_remaining(promo_remaining - 1)
                logger.info(f"Decremented promo_users_remaining to {promo_remaining - 1}")
            logger.info(f"Removed promo eligibility for user {user_id} after promo purchase")

        # Check if user has a pending generation to retry
        pending = context.user_data.get("pending_generation")
        if pending:
            keyboard = [[InlineKeyboardButton("🔄 Продолжить генерацию", callback_data="retry_generation")]]
            await update.message.reply_text(
                f"✅ *Оплата успешна!*\n\n"
                f"Начислено: *{tokens}* {_plural_gen(tokens)}\n"
                f"Ваш баланс: *{new_balance}* {_plural_gen(new_balance)}\n\n"
                f"Нажмите кнопку ниже чтобы продолжить генерацию.",
                reply_markup=InlineKeyboardMarkup(keyboard),
                parse_mode="Markdown"
            )
        else:
            await update.message.reply_text(
                f"✅ *Оплата успешна!*\n\n"
                f"Начислено: *{tokens}* {_plural_gen(tokens)}\n"
                f"Ваш баланс: *{new_balance}* {_plural_gen(new_balance)}\n\n"
                f"Отправьте ссылку на товар Wildberries для генерации видео.",
                parse_mode="Markdown"
            )
        logger.info(f"Credited {tokens} generations to user {user_id}, new balance: {new_balance}")
    else:
        # This should not happen, but handle it
        logger.error(f"Failed to credit generations to user {user_id}")
        await update.message.reply_text(
            "⚠️ Оплата получена, но произошла ошибка при начислении генераций.\n"
            "Пожалуйста, свяжитесь с поддержкой.\n\n"
            f"ID платежа: {telegram_payment_id}"
        )


async def balance_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Handle /balance command - show user's token balance.
    """
    user = update.effective_user
    user_id = user.id

    # Ensure user exists
    get_or_create_user(
        user_id,
        username=user.username,
        first_name=user.first_name,
        last_name=user.last_name
    )

    info = get_user_info(user_id)
    if not info:
        await update.message.reply_text("❌ Ошибка: пользователь не найден")
        return

    user_data = info["user"]
    balance = user_data["balance"]
    total_purchased = user_data["total_purchased"]
    total_spent = user_data["total_spent"]
    promo = user_data.get("is_promo_eligible", False) and get_promo_remaining() > 0

    text = f"💰 *Ваш баланс*\n\n"
    text += f"Генераций: *{balance}*\n"
    text += f"Куплено всего: {total_purchased}\n"
    text += f"Использовано: {total_spent}\n"

    if promo:
        text += "\n🎁 _У вас действует промо-скидка на покупки!_"

    # Show active gift packs
    active_packs = get_user_active_packs(user_id)
    if active_packs:
        import math
        for pack in active_packs:
            expires_at = datetime.strptime(pack["expires_at"], "%Y-%m-%d %H:%M:%S")
            delta = expires_at - datetime.now()
            total_seconds = delta.total_seconds()
            if total_seconds <= 0:
                continue
            days_left = math.ceil(total_seconds / 86400)
            if days_left <= 0:
                continue
            text += f"\n\n🎁 *Подарок: {pack['tokens']} {_plural_gen(pack['tokens'])}*\n"
            if days_left == 1:
                text += f"⚠️ *Последний день для активации\\!*\n"
            elif days_left < 5:
                text += f"⏰ Осталось {days_left} дня для активации\n"
            else:
                text += f"⏰ Осталось {days_left} дней для активации\n"
            text += f"_Отправьте ссылку на товар WB — подарок активируется автоматически_"

    # Show recent transactions
    transactions = info.get("transactions", [])
    if transactions:
        text += "\n\n📜 *Последние операции:*\n"
        for txn in transactions[:5]:
            sign = "+" if txn["amount"] > 0 else ""
            type_emoji = {
                "purchase": "💳",
                "generation": "🎬",
                "refund": "↩️",
                "admin_grant": "🎁",
                "admin_revoke": "⛔",
                "admin_credit": "🎁",
                "migration": "📦",
                "gift_pack_redeem": "🎁",
                "giveaway": "🎁",
            }.get(txn["type"], "•")
            # Escape underscores in type for Markdown
            txn_type = txn["type"].replace("_", "\\_")
            text += f"{type_emoji} {sign}{txn['amount']} ({txn_type})\n"

    keyboard = [[InlineKeyboardButton("💳 Купить генерации", callback_data="buy:show")]]

    await update.message.reply_text(
        text,
        reply_markup=InlineKeyboardMarkup(keyboard),
        parse_mode="Markdown"
    )


async def handle_buy_show(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Handle 'buy:show' callback from balance message."""
    query = update.callback_query
    await query.answer()

    user = query.from_user
    user_id = user.id

    # Check if user is promo-eligible AND promo is currently available
    promo = is_promo_eligible(user_id) and get_promo_remaining() > 0
    current_balance = get_balance(user_id)

    # Build message
    if promo:
        text = "🎁 *Специальное предложение для первой покупки!*\n\n"
        text += "⚡ Сниженные цены действуют только один раз.\n\n"
    else:
        text = "💳 *Покупка генераций*\n\n"
        text += "1 генерация = 1 видео\n\n"

    # Build keyboard with packages
    keyboard = []
    for pkg_id, pkg in PACKAGES.items():
        price = pkg["promo"] if promo else pkg["regular"]
        tokens = pkg["tokens"]

        per_gen = price / tokens / 100
        label = f"{tokens} {_plural_gen(tokens)} — {format_price(price)}"
        if tokens > 1:
            label += f" ({per_gen:.0f}₽/шт)"

        keyboard.append([
            InlineKeyboardButton(label, callback_data=f"buy:{pkg_id}")
        ])

    keyboard.append([
        InlineKeyboardButton("❌ Отмена", callback_data="buy:cancel")
    ])

    text += f"\n💰 Ваш баланс: *{current_balance}* {_plural_gen(current_balance)}"

    # Edit the existing message instead of replying
    await query.edit_message_text(
        text,
        reply_markup=InlineKeyboardMarkup(keyboard),
        parse_mode="Markdown"
    )


async def handle_retry_generation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Handle retry generation callback after payment."""
    query = update.callback_query
    await query.answer()

    pending = context.user_data.get("pending_generation")
    if not pending:
        await query.edit_message_text("❌ Нет сохранённой генерации для повтора.")
        return

    url = pending.get("url")
    if not url:
        await query.edit_message_text("❌ URL товара не найден.")
        context.user_data.pop("pending_generation", None)
        return

    # Clear pending and signal that we should process the URL
    context.user_data.pop("pending_generation", None)

    # Edit message to show we're processing
    await query.edit_message_text("🔄 Перезапускаю генерацию...")

    # Import and call handle_url from telegram_bot
    # We need to create a fake message-like update to pass the URL
    from telegram_bot import run_video_pipeline
    user = query.from_user
    chat_id = query.message.chat_id

    # Run the pipeline directly
    # Signature: run_video_pipeline(context, chat_id, url, user_id)
    await run_video_pipeline(context, chat_id, url, user.id)


# =============================================================================
# Handler Registration
# =============================================================================

def get_payment_handlers():
    """
    Get list of payment-related handlers to register with the application.

    Returns:
        List of handlers.
    """
    return [
        CommandHandler("buy", buy_command),
        CommandHandler("balance", balance_command),
        CallbackQueryHandler(handle_buy_callback, pattern=r"^buy:(?!show)"),
        CallbackQueryHandler(handle_buy_show, pattern=r"^buy:show$"),
        CallbackQueryHandler(handle_retry_generation, pattern=r"^retry_generation$"),
        PreCheckoutQueryHandler(precheckout_callback),
        MessageHandler(filters.SUCCESSFUL_PAYMENT, successful_payment_callback),
    ]


# =============================================================================
# CLI Test
# =============================================================================

if __name__ == "__main__":
    print("Payment handlers module")
    print(f"Provider token: {PAYMENT_PROVIDER_TOKEN[:20]}..." if PAYMENT_PROVIDER_TOKEN else "NOT SET")
    print(f"\nPricing:")
    for pkg_id, pkg in PACKAGES.items():
        print(f"  {pkg['tokens']} tokens: {format_price(pkg['regular'])} (promo: {format_price(pkg['promo'])})")
