import { UserRepository } from "../repositories/UserRepository.js";
import { encrypt } from "../utils/encryptionUtils.js";
import { ValidationError, UnauthorizedError } from "../errors/httpErrors.js";
import { UserDtoEntityMapper } from "../mappers/UserDtoEntityMapper.js";
import { LoginResponseDto } from "../dtos/LoginResponseDto.js";
import { LoginRequestDto } from "../dtos/LoginRequestDto.js";

/**
 * Controller responsible for authentication, e.g., login or issueing a token with extended
 * lifetime
 */
export class AuthController {
  constructor(
    private userRepository: UserRepository,
    private mapper: UserDtoEntityMapper
  ) {}

  /**
   * Login: Check user and password and generate token
   * @param loginRequest LoginRequestDto containing userName and password for login
   * @returns LoginResponse containing token and user data for the user who just logged in
   */
  async login(loginRequest : LoginRequestDto): Promise<LoginResponseDto> {
    const userName :string|undefined  = loginRequest.userName;
    const password :string|undefined = loginRequest.password;
    console.log("user", userName, " is trying to log in")
    if (!userName || !password) {
      throw new ValidationError("Username and password are required");
    }

    // Find user by userName
    const user = await this.userRepository.findByUserName(userName);
    // check user before trying to access password!
    if(!user){
      throw new UnauthorizedError("Invalid username or password");
    }
    // ensure user has an id - required to generate token
    const userId = user.id;
    if(user.id == undefined){
      throw new UnauthorizedError("Invalid username or password");
    }
    // Compare password
    const passwordMatches = encrypt.comparepassword(password, user.password);
    if (!passwordMatches) {
      throw new UnauthorizedError("Invalid username or password");
    }

    // Create JWT
    const tokenInfo = encrypt.generateToken({
      id: userId!, // ! to indicate that we've definitely checked for userId being defined
    });

    const responseDto = new LoginResponseDto();
    responseDto.userData = this.mapper.toDto(user);
    responseDto.token = tokenInfo.token;
    responseDto.expiryDate = tokenInfo.expiryDate;
    return responseDto;
  }
}
