"""
User management API routes.
"""
from typing import Optional, List, Dict
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select, func, or_, delete
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.deps import get_db, get_current_user
from app.core.response import success_response, paginated_response
from app.core.security import get_password_hash
from app.models.user import User, Role, UserRole
from app.schemas.user import UserCreate, UserUpdate, ResetPasswordRequest
from app.services.auth_service import auth_service

router = APIRouter()


def _status_to_is_active(status_value: Optional[str]) -> Optional[bool]:
    if not status_value:
        return None
    if status_value == "active":
        return True
    if status_value in {"disabled", "locked"}:
        return False
    return None


def _role_to_dict(role: Role) -> Dict:
    return {
        "id": role.id,
        "role_name": role.role_name,
        "role_code": role.role_code,
        "description": role.description,
        "status": role.status,
        "sort_order": role.sort_order,
        "created_at": role.created_at,
    }


def _user_to_dict(user: User, roles: List[Role]) -> Dict:
    return {
        "id": user.id,
        "username": user.username,
        "real_name": user.full_name or user.username,
        "email": user.email,
        "phone": user.phone,
        "avatar_url": user.avatar_url,
        "status": "active" if user.is_active else "disabled",
        "is_admin": user.is_superuser,
        "last_login_at": user.last_login_at,
        "created_at": user.created_at,
        "roles": [_role_to_dict(role) for role in roles],
    }


async def _ensure_roles_exist(db: AsyncSession, role_ids: List[int]) -> None:
    if not role_ids:
        return
    result = await db.execute(
        select(Role.id).where(Role.id.in_(role_ids)).where(Role.deleted_at.is_(None))
    )
    existing_ids = {row[0] for row in result.all()}
    missing = set(role_ids) - existing_ids
    if missing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Invalid role IDs: {sorted(missing)}",
        )


async def _set_user_roles(
    db: AsyncSession,
    user_id: int,
    role_ids: List[int],
    operator_id: Optional[int] = None,
) -> None:
    await db.execute(delete(UserRole).where(UserRole.user_id == user_id))
    if role_ids:
        for role_id in role_ids:
            db.add(UserRole(user_id=user_id, role_id=role_id, created_by=operator_id))


@router.get("/")
async def list_users(
    page: int = Query(1, ge=1, description="Page number"),
    page_size: int = Query(20, ge=1, le=100, description="Page size"),
    keyword: Optional[str] = Query(None, description="Search keyword"),
    status_value: Optional[str] = Query(None, alias="status", description="Status"),
    db: AsyncSession = Depends(get_db),
    current_user=Depends(get_current_user),
):
    is_active = _status_to_is_active(status_value)

    query = select(User)
    count_query = select(func.count(User.id))

    conditions = []
    if keyword:
        like_value = f"%{keyword}%"
        conditions.append(
            or_(
                User.username.ilike(like_value),
                User.full_name.ilike(like_value),
                User.phone.ilike(like_value),
                User.email.ilike(like_value),
            )
        )
    if is_active is not None:
        conditions.append(User.is_active == is_active)

    if conditions:
        query = query.where(*conditions)
        count_query = count_query.where(*conditions)

    query = query.order_by(User.id.desc())

    total_result = await db.execute(count_query)
    total = total_result.scalar() or 0

    offset = (page - 1) * page_size
    result = await db.execute(query.offset(offset).limit(page_size))
    users = list(result.scalars().all())

    role_map: Dict[int, List[Role]] = {user.id: [] for user in users}
    if users:
        user_ids = [user.id for user in users]
        role_result = await db.execute(
            select(UserRole.user_id, Role)
            .join(Role, Role.id == UserRole.role_id)
            .where(UserRole.user_id.in_(user_ids))
            .where(Role.deleted_at.is_(None))
        )
        for user_id, role in role_result.all():
            role_map.setdefault(user_id, []).append(role)

    items = [_user_to_dict(user, role_map.get(user.id, [])) for user in users]
    return paginated_response(items, total, page, page_size)


@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_user(
    payload: UserCreate,
    db: AsyncSession = Depends(get_db),
    current_user=Depends(get_current_user),
):
    existing = await db.execute(select(User).where(User.username == payload.username))
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Username already exists")

    email_value = payload.email
    if not email_value:
        email_value = f"{payload.username}@local.invalid"

    email_check = await db.execute(select(User).where(User.email == email_value))
    if email_check.scalar_one_or_none():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Email already exists")

    await _ensure_roles_exist(db, payload.role_ids)

    user = User(
        username=payload.username,
        email=email_value,
        hashed_password=get_password_hash(payload.password),
        full_name=payload.real_name,
        phone=payload.phone,
        is_active=True,
        is_superuser=False,
    )
    db.add(user)
    await db.flush()

    await _set_user_roles(db, user.id, payload.role_ids, current_user.id)
    await db.commit()
    await db.refresh(user)

    roles_result = await db.execute(
        select(Role)
        .join(UserRole, UserRole.role_id == Role.id)
        .where(UserRole.user_id == user.id)
        .where(Role.deleted_at.is_(None))
    )
    roles = list(roles_result.scalars().all())

    return success_response(data=_user_to_dict(user, roles))


@router.put("/{user_id}")
async def update_user(
    user_id: int,
    payload: UserUpdate,
    db: AsyncSession = Depends(get_db),
    current_user=Depends(get_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=status.HTTP_404_NOT_FOUND, detail="User not found")

    update_data = payload.model_dump(exclude_unset=True)

    if "real_name" in update_data:
        user.full_name = update_data.pop("real_name")

    if "status" in update_data:
        status_value = update_data.pop("status")
        is_active = _status_to_is_active(status_value)
        if is_active is not None:
            user.is_active = is_active

    if "email" in update_data:
        email_value = update_data.pop("email")
        if email_value:
            email_check = await db.execute(
                select(User).where(User.email == email_value).where(User.id != user_id)
            )
            if email_check.scalar_one_or_none():
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Email already exists")
            user.email = email_value

    if "phone" in update_data:
        user.phone = update_data.pop("phone")

    role_ids = update_data.pop("role_ids", None)
    if role_ids is not None:
        await _ensure_roles_exist(db, role_ids)
        await _set_user_roles(db, user.id, role_ids, current_user.id)

    db.add(user)
    await db.commit()
    await db.refresh(user)

    roles_result = await db.execute(
        select(Role)
        .join(UserRole, UserRole.role_id == Role.id)
        .where(UserRole.user_id == user.id)
        .where(Role.deleted_at.is_(None))
    )
    roles = list(roles_result.scalars().all())

    return success_response(data=_user_to_dict(user, roles))


@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    current_user=Depends(get_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=status.HTTP_404_NOT_FOUND, detail="User not found")

    await db.execute(delete(UserRole).where(UserRole.user_id == user_id))
    await db.delete(user)
    await db.commit()
    return success_response(message="Deleted")


@router.post("/{user_id}/reset-password")
async def reset_password(
    user_id: int,
    payload: ResetPasswordRequest,
    db: AsyncSession = Depends(get_db),
    current_user=Depends(get_current_user),
):
    success = await auth_service.reset_password(db=db, user_id=user_id, new_password=payload.new_password)
    if not success:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    return success_response(message="Password reset")
