from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from datetime import datetime, timezone import ulid from app.core.database.db_session import get_db from app.repositories.user_repository import user_repository from app.models.UserModel import User from app.models.RoleModel import Role from app.schemas.User import UserCreate, UserUpdate, UserResponse from app.core.permissions.RoleChecker import RoleChecker, PermissionChecker from app.core.validators.password_validator import validate_password_complexity from app.utils.Hash_util import hash_password from app.services.code_generator_service import code_generator_service router = APIRouter(prefix="/api/v1/users", tags=["User Creation"]) @router.post("/create", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def create_user( data: UserCreate, db: Session = Depends(get_db), current_user: User = Depends(PermissionChecker("user.create")) ): # 1. Check duplicate identity parameters duplicate = user_repository.check_duplicate_identity(db, data.email, data.phone) if duplicate: raise HTTPException(status_code=400, detail="User with this email or phone number already exists.") # 2. Check Role existence and fetch prefix role = db.get(Role, data.role_id) if not role or role.deleted_at is not None: raise HTTPException(status_code=400, detail="Specified role ID is invalid or inactive.") # 3. Validate password complexity validate_password_complexity(data.password) # 4. Generate sequential employee code prefix = role.role_prefix employee_code = code_generator_service.generate_next_code(db, "User", prefix) # 5. Build user record user_id = str(ulid.ULID()) new_user = User( user_id=user_id, employee_code=employee_code, first_name=data.first_name, last_name=data.last_name, display_name=data.display_name or f"{data.first_name} {data.last_name}", email=data.email, phone=data.phone, password_hash=hash_password(data.password), gender=data.gender, dob=data.dob, department_id=data.department_id, designation_id=data.designation_id, role_id=data.role_id, manager_id=data.manager_id, is_active=True ) user_repository.create(db, new_user) return new_user @router.get("/all", response_model=List[UserResponse]) def get_all_users( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(PermissionChecker("user.view")) ): users = user_repository.get_active_users(db, skip, limit) from app.models.AuditLogModel import AuditLog for u in users: latest = ( db.query(AuditLog) .filter(AuditLog.user_id == u.user_id) .order_by(AuditLog.created_at.desc()) .first() ) if latest: u.last_activity = f"{latest.action.replace('_', ' ').title()} {latest.entity_type}" else: u.last_activity = "No recent activity" return users @router.get("/profile/{user_id}", response_model=UserResponse) def get_user_profile( user_id: str, db: Session = Depends(get_db), current_user: User = Depends(PermissionChecker("user.view")) ): user = user_repository.get_by_id(db, user_id) if not user or user.deleted_at is not None: raise HTTPException(status_code=404, detail="User not found") return user @router.put("/update/{user_id}", response_model=UserResponse) def update_user_profile( user_id: str, payload: UserUpdate, db: Session = Depends(get_db), current_user: User = Depends(PermissionChecker("user.update")) ): user = user_repository.get_by_id(db, user_id) if not user or user.deleted_at is not None: raise HTTPException(status_code=404, detail="User not found") # Check email/phone uniqueness if being updated if payload.email and payload.email != user.email: if user_repository.get_by_email(db, payload.email): raise HTTPException(status_code=400, detail="Email is already registered.") user.email = payload.email if payload.phone and payload.phone != user.phone: if user_repository.get_by_phone(db, payload.phone): raise HTTPException(status_code=400, detail="Phone number is already registered.") user.phone = payload.phone # Update strings if payload.first_name: user.first_name = payload.first_name if payload.last_name: user.last_name = payload.last_name if payload.display_name: user.display_name = payload.display_name if payload.gender: user.gender = payload.gender if payload.dob: user.dob = payload.dob if payload.profile_image_id: user.profile_image_id = payload.profile_image_id # Update relationships if payload.department_id: user.department_id = payload.department_id if payload.designation_id: user.designation_id = payload.designation_id if payload.role_id: user.role_id = payload.role_id if payload.manager_id: user.manager_id = payload.manager_id # If resetting password if payload.password: validate_password_complexity(payload.password) user.password_hash = hash_password(payload.password) user_repository.update(db, user) return user @router.delete("/delete/{user_id}") def delete_user_profile( user_id: str, db: Session = Depends(get_db), current_user: User = Depends(PermissionChecker("user.delete")) ): user = user_repository.get_by_id(db, user_id) if not user or user.deleted_at is not None: raise HTTPException(status_code=404, detail="User not found") # Safe soft delete user.deleted_at = datetime.now(timezone.utc) user.is_active = False db.commit() return {"detail": "User soft deleted successfully"}