"""
CRUD операции для колеса удачи (Fortune Wheel).
"""

from datetime import UTC, datetime
from typing import Any

import structlog
from sqlalchemy import and_, desc, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.database.models import (
    WheelConfig,
    WheelPrize,
    WheelSpin,
)


logger = structlog.get_logger(__name__)


# ==================== WHEEL CONFIG ====================


async def get_wheel_config(db: AsyncSession) -> WheelConfig | None:
    """Получить текущую конфигурацию колеса (всегда id=1)."""
    result = await db.execute(select(WheelConfig).options(selectinload(WheelConfig.prizes)).where(WheelConfig.id == 1))
    return result.scalar_one_or_none()


async def get_or_create_wheel_config(db: AsyncSession) -> WheelConfig:
    """Получить или создать конфигурацию колеса."""
    config = await get_wheel_config(db)
    if config:
        return config

    # Создаем дефолтную конфигурацию
    config = WheelConfig(
        id=1,
        is_enabled=False,
        name='Колесо удачи',
        spin_cost_stars=10,
        spin_cost_days=1,
        spin_cost_stars_enabled=True,
        spin_cost_days_enabled=True,
        rtp_percent=80,
        daily_spin_limit=5,
        min_subscription_days_for_day_payment=3,
        promo_prefix='WHEEL',
        promo_validity_days=7,
    )
    db.add(config)
    await db.commit()
    await db.refresh(config)
    logger.info('🎡 Создана дефолтная конфигурация колеса удачи')
    return config


async def update_wheel_config(db: AsyncSession, **kwargs) -> WheelConfig:
    """Обновить конфигурацию колеса."""
    config = await get_or_create_wheel_config(db)

    for key, value in kwargs.items():
        if hasattr(config, key) and value is not None:
            setattr(config, key, value)

    config.updated_at = datetime.now(UTC)
    await db.commit()
    await db.refresh(config)
    logger.info('🎡 Обновлена конфигурация колеса', kwargs=kwargs)
    return config


# ==================== WHEEL PRIZES ====================


async def get_wheel_prizes(db: AsyncSession, config_id: int = 1, active_only: bool = True) -> list[WheelPrize]:
    """Получить список призов колеса."""
    query = select(WheelPrize).where(WheelPrize.config_id == config_id)

    if active_only:
        query = query.where(WheelPrize.is_active == True)

    query = query.order_by(WheelPrize.sort_order)

    result = await db.execute(query)
    return list(result.scalars().all())


async def get_wheel_prize_by_id(db: AsyncSession, prize_id: int) -> WheelPrize | None:
    """Получить приз по ID."""
    result = await db.execute(select(WheelPrize).where(WheelPrize.id == prize_id))
    return result.scalar_one_or_none()


async def create_wheel_prize(
    db: AsyncSession,
    config_id: int,
    prize_type: str,
    prize_value: int,
    display_name: str,
    prize_value_kopeks: int,
    emoji: str = '🎁',
    color: str = '#3B82F6',
    sort_order: int = 0,
    manual_probability: float | None = None,
    is_active: bool = True,
    promo_balance_bonus_kopeks: int = 0,
    promo_subscription_days: int = 0,
    promo_traffic_gb: int = 0,
) -> WheelPrize:
    """Создать новый приз на колесе."""
    prize = WheelPrize(
        config_id=config_id,
        prize_type=prize_type,
        prize_value=prize_value,
        display_name=display_name,
        prize_value_kopeks=prize_value_kopeks,
        emoji=emoji,
        color=color,
        sort_order=sort_order,
        manual_probability=manual_probability,
        is_active=is_active,
        promo_balance_bonus_kopeks=promo_balance_bonus_kopeks,
        promo_subscription_days=promo_subscription_days,
        promo_traffic_gb=promo_traffic_gb,
    )
    db.add(prize)
    await db.commit()
    await db.refresh(prize)
    logger.info('🎁 Создан приз колеса', display_name=display_name, prize_type=prize_type)
    return prize


async def update_wheel_prize(db: AsyncSession, prize_id: int, **kwargs) -> WheelPrize | None:
    """Обновить приз колеса."""
    prize = await get_wheel_prize_by_id(db, prize_id)
    if not prize:
        return None

    for key, value in kwargs.items():
        if hasattr(prize, key) and value is not None:
            setattr(prize, key, value)

    prize.updated_at = datetime.now(UTC)
    await db.commit()
    await db.refresh(prize)
    logger.info('🎁 Обновлен приз колеса ID', prize_id=prize_id, kwargs=kwargs)
    return prize


async def delete_wheel_prize(db: AsyncSession, prize_id: int) -> bool:
    """Удалить приз колеса."""
    prize = await get_wheel_prize_by_id(db, prize_id)
    if not prize:
        return False

    await db.delete(prize)
    await db.commit()
    logger.info('🗑️ Удален приз колеса ID', prize_id=prize_id)
    return True


async def reorder_wheel_prizes(db: AsyncSession, prize_ids: list[int]) -> bool:
    """Переупорядочить призы колеса."""
    for index, prize_id in enumerate(prize_ids):
        prize = await get_wheel_prize_by_id(db, prize_id)
        if prize:
            prize.sort_order = index

    await db.commit()
    logger.info('🔄 Переупорядочены призы колеса', prize_ids=prize_ids)
    return True


# ==================== WHEEL SPINS ====================


async def create_wheel_spin(
    db: AsyncSession,
    user_id: int,
    prize_id: int,
    payment_type: str,
    payment_amount: int,
    payment_value_kopeks: int,
    prize_type: str,
    prize_value: int,
    prize_display_name: str,
    prize_value_kopeks: int,
    generated_promocode_id: int | None = None,
    is_applied: bool = False,
) -> WheelSpin:
    """Создать запись о спине колеса."""
    spin = WheelSpin(
        user_id=user_id,
        prize_id=prize_id,
        payment_type=payment_type,
        payment_amount=payment_amount,
        payment_value_kopeks=payment_value_kopeks,
        prize_type=prize_type,
        prize_value=prize_value,
        prize_display_name=prize_display_name,
        prize_value_kopeks=prize_value_kopeks,
        generated_promocode_id=generated_promocode_id,
        is_applied=is_applied,
        applied_at=datetime.now(UTC) if is_applied else None,
    )
    db.add(spin)
    await db.commit()
    await db.refresh(spin)
    logger.info('🎰 Создан спин колеса: user_id=, prize', user_id=user_id, prize_display_name=prize_display_name)
    return spin


async def mark_spin_applied(db: AsyncSession, spin_id: int) -> WheelSpin | None:
    """Отметить спин как примененный."""
    result = await db.execute(select(WheelSpin).where(WheelSpin.id == spin_id))
    spin = result.scalar_one_or_none()
    if spin:
        spin.is_applied = True
        spin.applied_at = datetime.now(UTC)
        await db.commit()
        await db.refresh(spin)
    return spin


async def get_user_spins_today(db: AsyncSession, user_id: int) -> int:
    """Получить количество спинов пользователя за сегодня."""
    today_start = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0)

    result = await db.execute(
        select(func.count(WheelSpin.id)).where(
            and_(
                WheelSpin.user_id == user_id,
                WheelSpin.created_at >= today_start,
            )
        )
    )
    return result.scalar() or 0


async def get_user_spin_history(
    db: AsyncSession, user_id: int, limit: int = 20, offset: int = 0
) -> tuple[list[WheelSpin], int]:
    """Получить историю спинов пользователя."""
    # Общее количество
    count_result = await db.execute(select(func.count(WheelSpin.id)).where(WheelSpin.user_id == user_id))
    total = count_result.scalar() or 0

    # Спины с пагинацией (eager load prize relationship)
    result = await db.execute(
        select(WheelSpin)
        .options(selectinload(WheelSpin.prize))
        .where(WheelSpin.user_id == user_id)
        .order_by(desc(WheelSpin.created_at))
        .limit(limit)
        .offset(offset)
    )
    spins = list(result.scalars().all())

    return spins, total


async def get_all_spins(
    db: AsyncSession,
    user_id: int | None = None,
    date_from: datetime | None = None,
    date_to: datetime | None = None,
    limit: int = 50,
    offset: int = 0,
) -> tuple[list[WheelSpin], int]:
    """Получить все спины с фильтрами (для админки)."""
    conditions = []

    if user_id:
        conditions.append(WheelSpin.user_id == user_id)
    if date_from:
        conditions.append(WheelSpin.created_at >= date_from)
    if date_to:
        conditions.append(WheelSpin.created_at <= date_to)

    # Общее количество
    count_query = select(func.count(WheelSpin.id))
    if conditions:
        count_query = count_query.where(and_(*conditions))
    count_result = await db.execute(count_query)
    total = count_result.scalar() or 0

    # Спины с пагинацией
    query = select(WheelSpin).options(selectinload(WheelSpin.user))
    if conditions:
        query = query.where(and_(*conditions))
    query = query.order_by(desc(WheelSpin.created_at)).limit(limit).offset(offset)

    result = await db.execute(query)
    spins = list(result.scalars().all())

    return spins, total


# ==================== STATISTICS ====================


async def get_wheel_statistics(
    db: AsyncSession, date_from: datetime | None = None, date_to: datetime | None = None
) -> dict[str, Any]:
    """Получить статистику колеса удачи."""
    conditions = []
    if date_from:
        conditions.append(WheelSpin.created_at >= date_from)
    if date_to:
        conditions.append(WheelSpin.created_at <= date_to)

    base_query = select(WheelSpin)
    if conditions:
        base_query = base_query.where(and_(*conditions))

    # Общие метрики
    result = await db.execute(
        select(
            func.count(WheelSpin.id).label('total_spins'),
            func.coalesce(func.sum(WheelSpin.payment_value_kopeks), 0).label('total_revenue'),
            func.coalesce(func.sum(WheelSpin.prize_value_kopeks), 0).label('total_payout'),
        ).where(and_(*conditions) if conditions else True)
    )
    row = result.one()
    total_spins = row.total_spins or 0
    total_revenue = row.total_revenue or 0
    total_payout = row.total_payout or 0

    # Фактический RTP
    actual_rtp = (total_payout / total_revenue * 100) if total_revenue > 0 else 0

    # Распределение по типу оплаты
    payment_dist = await db.execute(
        select(
            WheelSpin.payment_type,
            func.count(WheelSpin.id).label('count'),
            func.sum(WheelSpin.payment_value_kopeks).label('total'),
        )
        .where(and_(*conditions) if conditions else True)
        .group_by(WheelSpin.payment_type)
    )
    spins_by_payment_type = {
        row.payment_type: {'count': row.count, 'total_kopeks': row.total or 0} for row in payment_dist
    }

    # Распределение призов
    prizes_dist = await db.execute(
        select(
            WheelSpin.prize_type,
            WheelSpin.prize_display_name,
            func.count(WheelSpin.id).label('count'),
            func.sum(WheelSpin.prize_value_kopeks).label('total'),
        )
        .where(and_(*conditions) if conditions else True)
        .group_by(WheelSpin.prize_type, WheelSpin.prize_display_name)
    )
    prizes_distribution = [
        {
            'prize_type': row.prize_type,
            'display_name': row.prize_display_name,
            'count': row.count,
            'total_kopeks': row.total or 0,
        }
        for row in prizes_dist
    ]

    # Топ выигрышей
    top_wins_result = await db.execute(
        select(WheelSpin)
        .options(selectinload(WheelSpin.user))
        .where(and_(*conditions) if conditions else True)
        .where(WheelSpin.prize_value_kopeks > 0)
        .order_by(desc(WheelSpin.prize_value_kopeks))
        .limit(10)
    )
    top_wins = [
        {
            'user_id': spin.user_id,
            'username': spin.user.username if spin.user else None,
            'prize_display_name': spin.prize_display_name,
            'prize_value_kopeks': spin.prize_value_kopeks,
            'created_at': spin.created_at.isoformat() if spin.created_at else None,
        }
        for spin in top_wins_result.scalars().all()
    ]

    # Конфигурация для сравнения
    config = await get_wheel_config(db)
    configured_rtp = config.rtp_percent if config else 80

    return {
        'total_spins': total_spins,
        'total_revenue_kopeks': total_revenue,
        'total_payout_kopeks': total_payout,
        'actual_rtp_percent': round(actual_rtp, 2),
        'configured_rtp_percent': configured_rtp,
        'spins_by_payment_type': spins_by_payment_type,
        'prizes_distribution': prizes_distribution,
        'top_wins': top_wins,
        'period_from': date_from.isoformat() if date_from else None,
        'period_to': date_to.isoformat() if date_to else None,
    }
