from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field
from sqlalchemy.exc import IntegrityError
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import User, UserRole, UserStatus
from app.core.security import get_client_permissions, get_allowed_routes, hash_password, require_admin


router = APIRouter(
    prefix="/api/users",
    tags=["users"],
    dependencies=[Depends(require_admin)],
)


def _clean_string_list(values: list[str]) -> list[str]:
    cleaned: list[str] = []
    for value in values:
        normalized = value.strip()
        if normalized:
            cleaned.append(normalized)
    return cleaned


def _normalize_username(username: str) -> str:
    normalized = username.strip()
    if not normalized:
        raise HTTPException(status_code=422, detail="Username cannot be blank")
    return normalized


def _serialize_user(user: User) -> "UserAdminResponse":
    return UserAdminResponse(
        id=user.id,
        username=user.username,
        display_name=user.display_name,
        role=user.role,
        status=user.status,
        permissions=get_client_permissions(user),
        allowed_permissions=user.allowed_permissions_json or [],
        denied_permissions=user.denied_permissions_json or [],
        allowed_routes=get_allowed_routes(user),
    )


def _get_user_or_404(db: Session, user_id: int) -> User:
    user = db.get(User, user_id)
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user


class UserAdminResponse(BaseModel):
    id: int
    username: str
    display_name: str | None
    role: UserRole
    status: UserStatus
    permissions: list[str]
    allowed_permissions: list[str] = Field(default_factory=list)
    denied_permissions: list[str] = Field(default_factory=list)
    allowed_routes: list[str] = Field(default_factory=list)


class UserListResponse(BaseModel):
    items: list[UserAdminResponse]


class UserCreateRequest(BaseModel):
    username: str = Field(min_length=1, max_length=100)
    password: str = Field(min_length=1, max_length=255)
    display_name: str | None = Field(default=None, max_length=100)
    role: UserRole
    status: UserStatus = UserStatus.active
    allowed_permissions: list[str] = Field(default_factory=list)
    denied_permissions: list[str] = Field(default_factory=list)
    allowed_routes: list[str] = Field(default_factory=list)


class UserUpdateRequest(BaseModel):
    display_name: str | None = Field(default=None, max_length=100)
    role: UserRole | None = None
    status: UserStatus | None = None
    allowed_permissions: list[str] | None = None
    denied_permissions: list[str] | None = None
    allowed_routes: list[str] | None = None


class ResetPasswordRequest(BaseModel):
    password: str = Field(min_length=1, max_length=255)


@router.get("", response_model=UserListResponse)
def list_users(db: Session = Depends(get_db)):
    users = db.execute(select(User).order_by(User.id)).scalars().all()
    return UserListResponse(items=[_serialize_user(user) for user in users])


@router.post("", response_model=UserAdminResponse, status_code=status.HTTP_201_CREATED)
def create_user(data: UserCreateRequest, db: Session = Depends(get_db)):
    normalized_username = _normalize_username(data.username)
    existing_user = db.execute(
        select(User).where(User.username == normalized_username)
    ).scalar_one_or_none()
    if existing_user is not None:
        raise HTTPException(status_code=400, detail="Username already exists")

    user = User(
        username=normalized_username,
        password_hash=hash_password(data.password),
        display_name=data.display_name.strip() if data.display_name else None,
        role=data.role,
        status=data.status,
        allowed_permissions_json=_clean_string_list(data.allowed_permissions),
        denied_permissions_json=_clean_string_list(data.denied_permissions),
        allowed_routes_json=_clean_string_list(data.allowed_routes),
    )
    db.add(user)
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=400, detail="Username already exists")
    db.refresh(user)
    return _serialize_user(user)


@router.put("/{user_id}", response_model=UserAdminResponse)
def update_user(user_id: int, data: UserUpdateRequest, db: Session = Depends(get_db)):
    user = _get_user_or_404(db, user_id)
    updates = data.model_dump(exclude_unset=True)

    if "display_name" in updates:
        user.display_name = updates["display_name"].strip() if updates["display_name"] else None
    if "role" in updates:
        user.role = updates["role"]
    if "status" in updates:
        user.status = updates["status"]
    if "allowed_permissions" in updates:
        user.allowed_permissions_json = _clean_string_list(updates["allowed_permissions"] or [])
    if "denied_permissions" in updates:
        user.denied_permissions_json = _clean_string_list(updates["denied_permissions"] or [])
    if "allowed_routes" in updates:
        user.allowed_routes_json = _clean_string_list(updates["allowed_routes"] or [])

    db.commit()
    db.refresh(user)
    return _serialize_user(user)


@router.post("/{user_id}/reset-password")
def reset_password(user_id: int, data: ResetPasswordRequest, db: Session = Depends(get_db)):
    user = _get_user_or_404(db, user_id)
    user.password_hash = hash_password(data.password)
    db.commit()
    return {"message": "Password reset successfully"}
