from __future__ import annotations

from typing import Any

import structlog
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased, selectinload

from app.database.crud.referral import get_user_referral_stats
from app.database.crud.user import (
    get_user_by_id,
    get_user_by_telegram_id,
    update_user,
)
from app.database.models import User
from app.services.partner_stats_service import PartnerStatsService
from app.utils.user_utils import (
    get_detailed_referral_list,
    get_effective_referral_commission_percent,
)

from ..dependencies import get_db_session, require_api_token
from ..schemas.partners import (
    ChangeData,
    DailyStats,
    DailyStatsResponse,
    EarningsByPeriod,
    GlobalPartnerStats,
    GlobalPartnerSummary,
    NewReferralsByPeriod,
    PartnerReferralCommissionUpdate,
    PartnerReferralItem,
    PartnerReferralList,
    PartnerReferrerDetail,
    PartnerReferrerItem,
    PartnerReferrerListResponse,
    PayoutsByPeriod,
    PeriodChange,
    PeriodComparisonResponse,
    PeriodData,
    ReferralsCountByPeriod,
    ReferrerDetailedStats,
    ReferrerSummary,
    TopReferralItem,
    TopReferralsResponse,
    TopReferrerItem,
    TopReferrersResponse,
)


logger = structlog.get_logger(__name__)

router = APIRouter()


def _apply_search_filter(query, search: str):
    search_lower = f'%{search.lower()}%'
    conditions = [
        func.lower(User.username).like(search_lower),
        func.lower(User.first_name).like(search_lower),
        func.lower(User.last_name).like(search_lower),
        func.lower(User.referral_code).like(search_lower),
    ]

    if search.isdigit():
        conditions.append(User.telegram_id == int(search))
        conditions.append(User.id == int(search))

    return query.where(or_(*conditions))


def _serialize_referrer(user: User, stats: dict) -> PartnerReferrerItem:
    total_earned_kopeks = int(stats.get('total_earned_kopeks') or 0)
    month_earned_kopeks = int(stats.get('month_earned_kopeks') or 0)

    return PartnerReferrerItem(
        id=user.id,
        telegram_id=user.telegram_id,
        username=user.username,
        first_name=user.first_name,
        last_name=user.last_name,
        referral_code=user.referral_code,
        referral_commission_percent=getattr(user, 'referral_commission_percent', None),
        effective_referral_commission_percent=get_effective_referral_commission_percent(user),
        invited_count=int(stats.get('invited_count') or 0),
        active_referrals=int(stats.get('active_referrals') or 0),
        total_earned_kopeks=total_earned_kopeks,
        total_earned_rubles=round(total_earned_kopeks / 100, 2),
        month_earned_kopeks=month_earned_kopeks,
        month_earned_rubles=round(month_earned_kopeks / 100, 2),
        created_at=user.created_at,
        last_activity=user.last_activity,
    )


def _serialize_referral_item(referral: dict) -> PartnerReferralItem:
    balance_kopeks = int(referral.get('balance_kopeks') or 0)
    total_earned_kopeks = int(referral.get('total_earned_kopeks') or 0)

    # Handle email-only users (telegram_id=None)
    raw_telegram_id = referral.get('telegram_id')
    telegram_id = int(raw_telegram_id) if raw_telegram_id is not None else None

    return PartnerReferralItem(
        id=int(referral.get('id')),
        telegram_id=telegram_id,
        full_name=str(referral.get('full_name')),
        username=referral.get('username'),
        created_at=referral.get('created_at'),
        last_activity=referral.get('last_activity'),
        has_made_first_topup=bool(referral.get('has_made_first_topup', False)),
        balance_kopeks=balance_kopeks,
        balance_rubles=round(balance_kopeks / 100, 2),
        total_earned_kopeks=total_earned_kopeks,
        total_earned_rubles=round(total_earned_kopeks / 100, 2),
        topups_count=int(referral.get('topups_count') or 0),
        days_since_registration=int(referral.get('days_since_registration') or 0),
        days_since_activity=referral.get('days_since_activity'),
        status=str(referral.get('status') or 'inactive'),
    )


@router.get('/referrers', response_model=PartnerReferrerListResponse)
async def list_referrers(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    search: str | None = Query(default=None),
) -> PartnerReferrerListResponse:
    referral_alias = aliased(User)
    has_referrals = select(referral_alias.id).where(referral_alias.referred_by_id == User.id).exists()

    base_query = (
        select(User).options(selectinload(User.referrer)).where(or_(User.referral_code.isnot(None), has_referrals))
    )

    if search:
        base_query = _apply_search_filter(base_query, search)

    total_query = base_query.with_only_columns(func.count()).order_by(None)
    total = await db.scalar(total_query) or 0

    result = await db.execute(base_query.order_by(User.created_at.desc()).offset(offset).limit(limit))
    referrers = result.scalars().unique().all()

    items: list[PartnerReferrerItem] = []
    for referrer in referrers:
        stats = await get_user_referral_stats(db, referrer.id)
        items.append(_serialize_referrer(referrer, stats))

    return PartnerReferrerListResponse(
        items=items,
        total=int(total),
        limit=limit,
        offset=offset,
    )


@router.get('/referrers/{user_id}', response_model=PartnerReferrerDetail)
async def get_referrer_detail(
    user_id: int,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
) -> PartnerReferrerDetail:
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    stats = await get_user_referral_stats(db, user.id)
    referrer_item = _serialize_referrer(user, stats)

    referrals_data = await get_detailed_referral_list(db, user.id, limit=limit, offset=offset)
    referral_items = [_serialize_referral_item(referral) for referral in referrals_data.get('referrals', [])]

    referrals_list = PartnerReferralList(
        items=referral_items,
        total=int(referrals_data.get('total_count') or 0),
        limit=limit,
        offset=offset,
        has_next=bool(referrals_data.get('has_next')),
        has_prev=bool(referrals_data.get('has_prev')),
        current_page=int(referrals_data.get('current_page') or 1),
        total_pages=int(referrals_data.get('total_pages') or 1),
    )

    return PartnerReferrerDetail(referrer=referrer_item, referrals=referrals_list)


@router.patch('/referrers/{user_id}/commission', response_model=PartnerReferrerItem)
async def update_referrer_commission(
    user_id: int,
    payload: PartnerReferralCommissionUpdate,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> PartnerReferrerItem:
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    await update_user(
        db,
        user,
        referral_commission_percent=payload.referral_commission_percent,
    )

    stats = await get_user_referral_stats(db, user.id)
    return _serialize_referrer(user, stats)


# ============================================================================
# РАСШИРЕННАЯ СТАТИСТИКА ПАРТНЁРОВ
# ============================================================================


@router.get('/stats', response_model=GlobalPartnerStats)
async def get_global_partner_stats(
    days: int = Query(30, ge=1, le=365),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> GlobalPartnerStats:
    """Глобальная статистика партнёрской программы."""
    data = await PartnerStatsService.get_global_partner_stats(db, days)

    return GlobalPartnerStats(
        summary=GlobalPartnerSummary(**data['summary']),
        payouts=PayoutsByPeriod(**data['payouts']),
        new_referrals=NewReferralsByPeriod(**data['new_referrals']),
    )


@router.get('/stats/daily', response_model=DailyStatsResponse)
async def get_global_daily_stats(
    days: int = Query(30, ge=1, le=365),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> DailyStatsResponse:
    """Глобальная статистика по дням."""
    data = await PartnerStatsService.get_global_daily_stats(db, days)

    return DailyStatsResponse(
        items=[DailyStats(**item) for item in data],
        days=days,
        user_id=None,
    )


@router.get('/stats/top-referrers', response_model=TopReferrersResponse)
async def get_top_referrers(
    limit: int = Query(10, ge=1, le=100),
    days: int | None = Query(None, ge=1, le=365),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> TopReferrersResponse:
    """Топ рефереров по заработку."""
    data = await PartnerStatsService.get_top_referrers(db, limit, days)

    return TopReferrersResponse(
        items=[TopReferrerItem(**item) for item in data],
        days=days,
    )


@router.get('/referrers/{user_id}/stats', response_model=ReferrerDetailedStats)
async def get_referrer_detailed_stats(
    user_id: int,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> ReferrerDetailedStats:
    """Детальная статистика реферера."""
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    data = await PartnerStatsService.get_referrer_detailed_stats(db, user.id)

    return ReferrerDetailedStats(
        user_id=data['user_id'],
        summary=ReferrerSummary(**data['summary']),
        earnings=EarningsByPeriod(**data['earnings']),
        referrals_count=ReferralsCountByPeriod(**data['referrals_count']),
    )


@router.get('/referrers/{user_id}/stats/daily', response_model=DailyStatsResponse)
async def get_referrer_daily_stats(
    user_id: int,
    days: int = Query(30, ge=1, le=365),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> DailyStatsResponse:
    """Статистика реферера по дням."""
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    data = await PartnerStatsService.get_referrer_daily_stats(db, user.id, days)

    return DailyStatsResponse(
        items=[DailyStats(**item) for item in data],
        days=days,
        user_id=user.id,
    )


@router.get('/referrers/{user_id}/stats/top-referrals', response_model=TopReferralsResponse)
async def get_referrer_top_referrals(
    user_id: int,
    limit: int = Query(10, ge=1, le=100),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> TopReferralsResponse:
    """Топ рефералов реферера по принесённому доходу."""
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    data = await PartnerStatsService.get_referrer_top_referrals(db, user.id, limit)

    return TopReferralsResponse(
        items=[TopReferralItem(**item) for item in data],
        user_id=user.id,
    )


@router.get('/referrers/{user_id}/stats/compare', response_model=PeriodComparisonResponse)
async def get_referrer_period_comparison(
    user_id: int,
    current_days: int = Query(7, ge=1, le=365),
    previous_days: int = Query(7, ge=1, le=365),
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> PeriodComparisonResponse:
    """Сравнение периодов для реферера."""
    user = await get_user_by_telegram_id(db, user_id)
    if not user:
        user = await get_user_by_id(db, user_id)

    if not user:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'User not found')

    data = await PartnerStatsService.get_referrer_period_comparison(db, user.id, current_days, previous_days)

    return PeriodComparisonResponse(
        current_period=PeriodData(**data['current_period']),
        previous_period=PeriodData(**data['previous_period']),
        change=PeriodChange(
            referrals=ChangeData(**data['change']['referrals']),
            earnings=ChangeData(**data['change']['earnings']),
        ),
        user_id=user.id,
    )
