from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.dependencies import require_admin
from app.core.security import hash_password
from app.database.session import get_db
from app.models.project import Project
from app.models.user import User
from app.schemas.admin import (
    AdminProjectResponse,
    AdminSetupRequest,
    AdminUserResponse,
    AdminUserUpdateRequest,
)
from app.utils.slug import is_valid_username

router = APIRouter()


# ── Setup ─────────────────────────────────────────────────────────────────────

@router.get("/setup/status")
async def setup_status(db: AsyncSession = Depends(get_db)):
    """Check whether any admin user exists."""
    result = await db.execute(select(User).where(User.is_admin == True))  # noqa: E712
    has_admin = result.scalar_one_or_none() is not None
    return {"has_admin": has_admin}


@router.post("/setup", response_model=AdminUserResponse, status_code=status.HTTP_201_CREATED)
async def setup_admin(body: AdminSetupRequest, db: AsyncSession = Depends(get_db)):
    """Create the first admin user. Fails if an admin already exists."""
    existing_admin = await db.execute(select(User).where(User.is_admin == True))  # noqa: E712
    if existing_admin.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Admin already configured.")

    username = body.username.lower().strip()
    if not is_valid_username(username):
        raise HTTPException(
            status_code=400,
            detail="Username must be 3-30 chars, only lowercase letters/numbers/underscores/hyphens.",
        )
    if len(body.password) < 8:
        raise HTTPException(status_code=400, detail="Password must be at least 8 characters.")

    # Check uniqueness
    conflict = await db.execute(
        select(User).where((User.username == username) | (User.email == body.email))
    )
    if conflict.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Username or email already taken.")

    user = User(
        username=username,
        email=body.email,
        hashed_password=hash_password(body.password),
        is_admin=True,
        is_active=True,
    )
    db.add(user)
    await db.commit()
    await db.refresh(user)

    count_result = await db.execute(
        select(func.count()).where(Project.user_id == user.id)
    )
    project_count = count_result.scalar() or 0

    return AdminUserResponse(
        id=user.id,
        username=user.username,
        email=user.email,
        avatar_url=user.avatar_url,
        is_active=user.is_active,
        is_admin=user.is_admin,
        created_at=user.created_at,
        project_count=project_count,
    )


# ── Users ─────────────────────────────────────────────────────────────────────

async def _user_with_count(db: AsyncSession, user: User) -> AdminUserResponse:
    count_result = await db.execute(
        select(func.count()).where(Project.user_id == user.id)
    )
    project_count = count_result.scalar() or 0
    return AdminUserResponse(
        id=user.id,
        username=user.username,
        email=user.email,
        avatar_url=user.avatar_url,
        is_active=user.is_active,
        is_admin=user.is_admin,
        created_at=user.created_at,
        project_count=project_count,
    )


@router.get("/users", response_model=list[AdminUserResponse])
async def list_users(
    db: AsyncSession = Depends(get_db),
    _admin: User = Depends(require_admin),
):
    result = await db.execute(select(User).order_by(User.created_at.desc()))
    users = result.scalars().all()
    return [await _user_with_count(db, u) for u in users]


@router.get("/users/{user_id}", response_model=AdminUserResponse)
async def get_user(
    user_id: str,
    db: AsyncSession = Depends(get_db),
    _admin: User = Depends(require_admin),
):
    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.")
    return await _user_with_count(db, user)


@router.put("/users/{user_id}", response_model=AdminUserResponse)
async def update_user(
    user_id: str,
    body: AdminUserUpdateRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(require_admin),
):
    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.")

    if body.username is not None:
        new_username = body.username.lower().strip()
        if not is_valid_username(new_username):
            raise HTTPException(status_code=400, detail="Invalid username format.")
        if new_username != user.username:
            conflict = await db.execute(select(User).where(User.username == new_username))
            if conflict.scalar_one_or_none():
                raise HTTPException(status_code=400, detail="Username already taken.")
        user.username = new_username

    if body.email is not None:
        if body.email != user.email:
            conflict = await db.execute(select(User).where(User.email == body.email))
            if conflict.scalar_one_or_none():
                raise HTTPException(status_code=400, detail="Email already in use.")
        user.email = body.email

    if body.password is not None:
        if len(body.password) < 8:
            raise HTTPException(status_code=400, detail="Password must be at least 8 characters.")
        user.hashed_password = hash_password(body.password)

    if body.is_active is not None:
        user.is_active = body.is_active

    if body.is_admin is not None:
        # Prevent removing admin from yourself
        if user.id == admin.id and not body.is_admin:
            raise HTTPException(status_code=400, detail="Cannot remove your own admin privileges.")
        user.is_admin = body.is_admin

    await db.commit()
    await db.refresh(user)
    return await _user_with_count(db, user)


@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
    user_id: str,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(require_admin),
):
    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.")
    if user.id == admin.id:
        raise HTTPException(status_code=400, detail="Cannot delete your own account.")

    # Delete all user's projects first
    projects_result = await db.execute(select(Project).where(Project.user_id == user_id))
    for project in projects_result.scalars().all():
        await db.delete(project)

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


# ── Projects ──────────────────────────────────────────────────────────────────

@router.get("/projects", response_model=list[AdminProjectResponse])
async def list_all_projects(
    db: AsyncSession = Depends(get_db),
    _admin: User = Depends(require_admin),
):
    result = await db.execute(
        select(Project, User.username)
        .join(User, User.id == Project.user_id)
        .order_by(Project.created_at.desc())
    )
    rows = result.all()
    return [
        AdminProjectResponse(
            id=project.id,
            name=project.name,
            slug=project.slug,
            description=project.description,
            is_public=project.is_public,
            board_type=project.board_type,
            owner_username=username,
            owner_id=project.user_id,
            created_at=project.created_at,
            updated_at=project.updated_at,
        )
        for project, username in rows
    ]


@router.delete("/projects/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_project(
    project_id: str,
    db: AsyncSession = Depends(get_db),
    _admin: User = Depends(require_admin),
):
    result = await db.execute(select(Project).where(Project.id == project_id))
    project = result.scalar_one_or_none()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found.")
    await db.delete(project)
    await db.commit()
