"""
Promo Code API endpoints - user redemption and admin management
"""
import secrets
import string
from datetime import datetime

from fastapi import APIRouter, HTTPException
from sqlalchemy import select
from sqlalchemy.orm import selectinload

from app.api.deps import CurrentUser, DbSession, require_admin_with_2fa
from app.models import User, CoinTransaction, CoinTransactionType
from app.models.promo_code import PromoCode, PromoCodeRedemption
from app.schemas.promo_code import (
    PromoCodeCreate,
    PromoCodeUpdate,
    PromoCodeResponse,
    PromoCodeRedeemRequest,
    PromoCodeRedeemResponse,
    PromoCodeRedemptionResponse,
    PromoCodeRedemptionUser,
)
from app.schemas.common import MessageResponse

router = APIRouter(prefix="/promo", tags=["promo"])


def generate_promo_code(length: int = 8) -> str:
    """Generate a random promo code"""
    chars = string.ascii_uppercase + string.digits
    return ''.join(secrets.choice(chars) for _ in range(length))


# === User endpoints ===

@router.post("/redeem", response_model=PromoCodeRedeemResponse)
async def redeem_promo_code(
    data: PromoCodeRedeemRequest,
    current_user: CurrentUser,
    db: DbSession,
):
    """Redeem a promo code to receive coins"""
    # Find promo code
    result = await db.execute(
        select(PromoCode).where(PromoCode.code == data.code.upper().strip())
    )
    promo = result.scalar_one_or_none()

    if not promo:
        raise HTTPException(status_code=404, detail="Промокод не найден")

    # Check if valid
    if not promo.is_active:
        raise HTTPException(status_code=400, detail="Промокод деактивирован")

    now = datetime.utcnow()
    if promo.valid_from and now < promo.valid_from:
        raise HTTPException(status_code=400, detail="Промокод ещё не активен")

    if promo.valid_until and now > promo.valid_until:
        raise HTTPException(status_code=400, detail="Промокод истёк")

    if promo.max_uses is not None and promo.uses_count >= promo.max_uses:
        raise HTTPException(status_code=400, detail="Лимит использований исчерпан")

    # Check if user already redeemed
    result = await db.execute(
        select(PromoCodeRedemption).where(
            PromoCodeRedemption.promo_code_id == promo.id,
            PromoCodeRedemption.user_id == current_user.id,
        )
    )
    existing = result.scalar_one_or_none()

    if existing:
        raise HTTPException(status_code=400, detail="Вы уже использовали этот промокод")

    # Create redemption record
    redemption = PromoCodeRedemption(
        promo_code_id=promo.id,
        user_id=current_user.id,
        coins_awarded=promo.coins_amount,
    )
    db.add(redemption)

    # Update uses count
    promo.uses_count += 1

    # Award coins
    transaction = CoinTransaction(
        user_id=current_user.id,
        amount=promo.coins_amount,
        transaction_type=CoinTransactionType.PROMO_CODE.value,
        reference_type="promo_code",
        reference_id=promo.id,
        description=f"Промокод: {promo.code}",
    )
    db.add(transaction)

    current_user.coins_balance += promo.coins_amount

    await db.commit()
    await db.refresh(current_user)

    return PromoCodeRedeemResponse(
        success=True,
        coins_awarded=promo.coins_amount,
        new_balance=current_user.coins_balance,
        message=f"Вы получили {promo.coins_amount} монет!",
    )


# === Admin endpoints ===

@router.get("/admin/list", response_model=list[PromoCodeResponse])
async def admin_list_promo_codes(
    current_user: CurrentUser,
    db: DbSession,
    include_inactive: bool = False,
):
    """Get all promo codes (admin only)"""
    require_admin_with_2fa(current_user)

    query = select(PromoCode).options(selectinload(PromoCode.created_by))
    if not include_inactive:
        query = query.where(PromoCode.is_active == True)

    query = query.order_by(PromoCode.created_at.desc())

    result = await db.execute(query)
    promos = result.scalars().all()

    return [
        PromoCodeResponse(
            id=p.id,
            code=p.code,
            coins_amount=p.coins_amount,
            max_uses=p.max_uses,
            uses_count=p.uses_count,
            is_active=p.is_active,
            valid_from=p.valid_from,
            valid_until=p.valid_until,
            created_at=p.created_at,
            created_by_nickname=p.created_by.nickname if p.created_by else None,
        )
        for p in promos
    ]


@router.post("/admin/create", response_model=PromoCodeResponse)
async def admin_create_promo_code(
    data: PromoCodeCreate,
    current_user: CurrentUser,
    db: DbSession,
):
    """Create a new promo code (admin only)"""
    require_admin_with_2fa(current_user)

    # Generate or use provided code
    code = data.code.upper().strip() if data.code else generate_promo_code()

    # Check uniqueness
    result = await db.execute(
        select(PromoCode).where(PromoCode.code == code)
    )
    if result.scalar_one_or_none():
        raise HTTPException(status_code=400, detail=f"Промокод '{code}' уже существует")

    promo = PromoCode(
        code=code,
        coins_amount=data.coins_amount,
        max_uses=data.max_uses,
        valid_from=data.valid_from,
        valid_until=data.valid_until,
        created_by_id=current_user.id,
    )
    db.add(promo)
    await db.commit()
    await db.refresh(promo)

    return PromoCodeResponse(
        id=promo.id,
        code=promo.code,
        coins_amount=promo.coins_amount,
        max_uses=promo.max_uses,
        uses_count=promo.uses_count,
        is_active=promo.is_active,
        valid_from=promo.valid_from,
        valid_until=promo.valid_until,
        created_at=promo.created_at,
        created_by_nickname=current_user.nickname,
    )


@router.put("/admin/{promo_id}", response_model=PromoCodeResponse)
async def admin_update_promo_code(
    promo_id: int,
    data: PromoCodeUpdate,
    current_user: CurrentUser,
    db: DbSession,
):
    """Update a promo code (admin only)"""
    require_admin_with_2fa(current_user)

    result = await db.execute(
        select(PromoCode)
        .options(selectinload(PromoCode.created_by))
        .where(PromoCode.id == promo_id)
    )
    promo = result.scalar_one_or_none()

    if not promo:
        raise HTTPException(status_code=404, detail="Промокод не найден")

    if data.is_active is not None:
        promo.is_active = data.is_active
    if data.max_uses is not None:
        promo.max_uses = data.max_uses
    if data.valid_until is not None:
        promo.valid_until = data.valid_until

    await db.commit()
    await db.refresh(promo)

    return PromoCodeResponse(
        id=promo.id,
        code=promo.code,
        coins_amount=promo.coins_amount,
        max_uses=promo.max_uses,
        uses_count=promo.uses_count,
        is_active=promo.is_active,
        valid_from=promo.valid_from,
        valid_until=promo.valid_until,
        created_at=promo.created_at,
        created_by_nickname=promo.created_by.nickname if promo.created_by else None,
    )


@router.delete("/admin/{promo_id}", response_model=MessageResponse)
async def admin_delete_promo_code(
    promo_id: int,
    current_user: CurrentUser,
    db: DbSession,
):
    """Delete a promo code (admin only)"""
    require_admin_with_2fa(current_user)

    result = await db.execute(
        select(PromoCode).where(PromoCode.id == promo_id)
    )
    promo = result.scalar_one_or_none()

    if not promo:
        raise HTTPException(status_code=404, detail="Промокод не найден")

    await db.delete(promo)
    await db.commit()

    return MessageResponse(message=f"Промокод '{promo.code}' удалён")


@router.get("/admin/{promo_id}/redemptions", response_model=list[PromoCodeRedemptionResponse])
async def admin_get_promo_redemptions(
    promo_id: int,
    current_user: CurrentUser,
    db: DbSession,
):
    """Get list of users who redeemed a promo code (admin only)"""
    require_admin_with_2fa(current_user)

    # Check promo exists
    result = await db.execute(
        select(PromoCode).where(PromoCode.id == promo_id)
    )
    if not result.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Промокод не найден")

    # Get redemptions
    result = await db.execute(
        select(PromoCodeRedemption)
        .options(selectinload(PromoCodeRedemption.user))
        .where(PromoCodeRedemption.promo_code_id == promo_id)
        .order_by(PromoCodeRedemption.redeemed_at.desc())
    )
    redemptions = result.scalars().all()

    return [
        PromoCodeRedemptionResponse(
            id=r.id,
            user=PromoCodeRedemptionUser(
                id=r.user.id,
                nickname=r.user.nickname,
            ),
            coins_awarded=r.coins_awarded,
            redeemed_at=r.redeemed_at,
        )
        for r in redemptions
    ]
