from fastapi import APIRouter, HTTPException, Query
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from pydantic import BaseModel, Field

from app.api.deps import DbSession, CurrentUser, require_admin
from app.models import User, UserRole, Marathon, Participant, Game
from app.schemas import UserPublic, MarathonListItem, MessageResponse

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


class SetUserRole(BaseModel):
    role: str = Field(..., pattern="^(user|admin)$")


class AdminUserResponse(BaseModel):
    id: int
    login: str
    nickname: str
    role: str
    avatar_url: str | None = None
    telegram_id: int | None = None
    telegram_username: str | None = None
    marathons_count: int = 0
    created_at: str

    class Config:
        from_attributes = True


class AdminMarathonResponse(BaseModel):
    id: int
    title: str
    status: str
    creator: UserPublic
    participants_count: int
    games_count: int
    start_date: str | None
    end_date: str | None
    created_at: str

    class Config:
        from_attributes = True


@router.get("/users", response_model=list[AdminUserResponse])
async def list_users(
    current_user: CurrentUser,
    db: DbSession,
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=100),
    search: str | None = None,
):
    """List all users. Admin only."""
    require_admin(current_user)

    query = select(User).order_by(User.created_at.desc())

    if search:
        query = query.where(
            (User.login.ilike(f"%{search}%")) |
            (User.nickname.ilike(f"%{search}%"))
        )

    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    users = result.scalars().all()

    response = []
    for user in users:
        # Count marathons user participates in
        marathons_count = await db.scalar(
            select(func.count()).select_from(Participant).where(Participant.user_id == user.id)
        )
        response.append(AdminUserResponse(
            id=user.id,
            login=user.login,
            nickname=user.nickname,
            role=user.role,
            avatar_url=user.avatar_url,
            telegram_id=user.telegram_id,
            telegram_username=user.telegram_username,
            marathons_count=marathons_count,
            created_at=user.created_at.isoformat(),
        ))

    return response


@router.get("/users/{user_id}", response_model=AdminUserResponse)
async def get_user(user_id: int, current_user: CurrentUser, db: DbSession):
    """Get user details. Admin only."""
    require_admin(current_user)

    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    marathons_count = await db.scalar(
        select(func.count()).select_from(Participant).where(Participant.user_id == user.id)
    )

    return AdminUserResponse(
        id=user.id,
        login=user.login,
        nickname=user.nickname,
        role=user.role,
        avatar_url=user.avatar_url,
        telegram_id=user.telegram_id,
        telegram_username=user.telegram_username,
        marathons_count=marathons_count,
        created_at=user.created_at.isoformat(),
    )


@router.patch("/users/{user_id}/role", response_model=AdminUserResponse)
async def set_user_role(
    user_id: int,
    data: SetUserRole,
    current_user: CurrentUser,
    db: DbSession,
):
    """Set user's global role. Admin only."""
    require_admin(current_user)

    # Cannot change own role
    if user_id == current_user.id:
        raise HTTPException(status_code=400, detail="Cannot change your own role")

    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    user.role = data.role
    await db.commit()
    await db.refresh(user)

    marathons_count = await db.scalar(
        select(func.count()).select_from(Participant).where(Participant.user_id == user.id)
    )

    return AdminUserResponse(
        id=user.id,
        login=user.login,
        nickname=user.nickname,
        role=user.role,
        avatar_url=user.avatar_url,
        telegram_id=user.telegram_id,
        telegram_username=user.telegram_username,
        marathons_count=marathons_count,
        created_at=user.created_at.isoformat(),
    )


@router.delete("/users/{user_id}", response_model=MessageResponse)
async def delete_user(user_id: int, current_user: CurrentUser, db: DbSession):
    """Delete a user. Admin only."""
    require_admin(current_user)

    # Cannot delete yourself
    if user_id == current_user.id:
        raise HTTPException(status_code=400, detail="Cannot delete yourself")

    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    # Cannot delete another admin
    if user.role == UserRole.ADMIN.value:
        raise HTTPException(status_code=400, detail="Cannot delete another admin")

    await db.delete(user)
    await db.commit()

    return MessageResponse(message="User deleted")


@router.get("/marathons", response_model=list[AdminMarathonResponse])
async def list_marathons(
    current_user: CurrentUser,
    db: DbSession,
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=100),
    search: str | None = None,
):
    """List all marathons. Admin only."""
    require_admin(current_user)

    query = (
        select(Marathon)
        .options(selectinload(Marathon.creator))
        .order_by(Marathon.created_at.desc())
    )

    if search:
        query = query.where(Marathon.title.ilike(f"%{search}%"))

    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    marathons = result.scalars().all()

    response = []
    for marathon in marathons:
        participants_count = await db.scalar(
            select(func.count()).select_from(Participant).where(Participant.marathon_id == marathon.id)
        )
        games_count = await db.scalar(
            select(func.count()).select_from(Game).where(Game.marathon_id == marathon.id)
        )
        response.append(AdminMarathonResponse(
            id=marathon.id,
            title=marathon.title,
            status=marathon.status,
            creator=UserPublic.model_validate(marathon.creator),
            participants_count=participants_count,
            games_count=games_count,
            start_date=marathon.start_date.isoformat() if marathon.start_date else None,
            end_date=marathon.end_date.isoformat() if marathon.end_date else None,
            created_at=marathon.created_at.isoformat(),
        ))

    return response


@router.delete("/marathons/{marathon_id}", response_model=MessageResponse)
async def delete_marathon(marathon_id: int, current_user: CurrentUser, db: DbSession):
    """Delete a marathon. Admin only."""
    require_admin(current_user)

    result = await db.execute(select(Marathon).where(Marathon.id == marathon_id))
    marathon = result.scalar_one_or_none()
    if not marathon:
        raise HTTPException(status_code=404, detail="Marathon not found")

    await db.delete(marathon)
    await db.commit()

    return MessageResponse(message="Marathon deleted")


@router.get("/stats")
async def get_stats(current_user: CurrentUser, db: DbSession):
    """Get platform statistics. Admin only."""
    require_admin(current_user)

    users_count = await db.scalar(select(func.count()).select_from(User))
    marathons_count = await db.scalar(select(func.count()).select_from(Marathon))
    games_count = await db.scalar(select(func.count()).select_from(Game))
    participants_count = await db.scalar(select(func.count()).select_from(Participant))

    return {
        "users_count": users_count,
        "marathons_count": marathons_count,
        "games_count": games_count,
        "total_participations": participants_count,
    }
