import { ValidationError, ConflictError, NotFoundError } from "../errors/httpErrors.js";
import { CreateUserRequestDto } from "../dtos/CreateUserRequestDto.js";
import { UserDto } from "../dtos/UserDto.js";
import { encrypt } from "../utils/encryptionUtils.js";
import { UserRepository } from "../repositories/UserRepository.js";
import { UserDtoEntityMapper } from "../mappers/UserDtoEntityMapper.js";
import { UUID } from "crypto";

/**
 * Controls all user specific actions
 */
export class UserController {
  constructor(
    private userRepository: UserRepository,
    private mapper: UserDtoEntityMapper
  ) {}

  /**
   * Create a new user
   * @param dto CreateUserRequestDto containing data for the user to add
   * @returns UserDto Data of the user as stored in the database
   */
  async createUser(dto: CreateUserRequestDto): Promise<UserDto> {
    // check mandatory fields
    if(!dto.userData){
      throw new ValidationError("User data is required")
    }
    const email = dto.userData.email;
    if (!email || (email && email.length == 0)) {
      throw new ValidationError("Email is required");
    }
    const password = dto.password;
    if(!password || (password && password.length == 0)){
      throw new ValidationError("Password is required");
    }
    const userName = dto.userData.userName;
    if (!userName|| (userName && userName.length == 0)){
      throw new ValidationError("Username is required");
    }
  
    // user name must be unique
    const existingUser = await this.userRepository.findByUserName(userName);
    if (existingUser) {
      throw new ConflictError("User with this user name already exists");
    }

    const userEntity = this.mapper.toEntity(dto.userData);
    userEntity.password = await encrypt.encryptpass(password);

    const savedUser = await this.userRepository.create(userEntity);

    return this.mapper.toDto(savedUser);
  }

  /**
   * Load data of a specific user
   * @param userId Id of user to load
   * @returns UserDto containing the user's data
   */
  async getUserById(userId: UUID|string|undefined): Promise<UserDto> {
    if(!userId){
      throw new ValidationError("userId is required");
    }

    const userEntity = await this.userRepository.findById(userId);
    if(!userEntity){
      throw new NotFoundError("user with id" + userId + "not found!")
    }
    return this.mapper.toDto(userEntity);
  }
}
