import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from 'src/user/entities/user.entity';
import { Repository } from 'typeorm';
import { UserRepository } from 'src/user/user.repository';
import { LoginDto } from './dto/login.dto';
import { generateRandomString, isEmpty } from 'src/common/helper';
import { lan } from 'src/lan';
import * as bcrypt from 'bcrypt';
import * as moment from 'moment';
import { ChangePasswordDto } from './dto/change-password.dto';
import isValidUuidV4 from 'src/common/uuid validator/uuid-validate';
import { UpdateAuthDto } from './dto/update-auth.dto';

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly user: Repository<UserEntity>,
    private readonly userRepository: UserRepository,
  ) {}

  async login(loginDto: LoginDto) {
    const { email, password } = loginDto;

    const user = await this.userRepository.findOneForPassword(email);
    if (isEmpty(user)) {
      throw new NotFoundException(lan('login.user_not_found'));
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);

    if (!isPasswordValid) {
      throw new UnauthorizedException(lan('login.invalid_password'));
    }

    const randomString = generateRandomString(200);
    const encodedString = Buffer.from(randomString).toString('base64');
    const timestamp = moment().format();
    const stringWithTimestamp = encodedString + timestamp.toString();
    const access_token = Buffer.from(stringWithTimestamp).toString('base64');

    const accessTokenExpireToMinutes =
      Number(process.env.ACCESS_TOKEN_EXPIRE || 24) * 60;

    // Set the token's expiration time to be the current time plus desired time
    const access_token_expiry = moment
      .utc()
      .add(accessTokenExpireToMinutes, 'minutes')
      .format();

    // Save the token and expiration time in the database
    user.access_token = access_token;
    user.access_token_expiry = access_token_expiry;

    await this.user.save(user);
    const data = await this.userRepository.findByEmail(email);

    return data;
  }

  async changePassword(changePassDto: ChangePasswordDto) {
    const { email, password, new_password, confirm_password } = changePassDto;

    const user = await this.userRepository.findOneForPassword(email);

    if (isEmpty(user)) {
      throw new NotFoundException(lan('login.user_not_found'));
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);

    if (!isPasswordValid) {
      throw new UnauthorizedException(
        lan('change.password.invalid_old_password'),
      );
    }

    if (new_password !== confirm_password) {
      throw new BadRequestException(
        lan('change.password.password_do_not_match'),
      );
    }

    const hashedNewPassword = await bcrypt.hash(new_password, 10);
    user.password = hashedNewPassword;

    await this.user.save(user);
    const data = await this.userRepository.findByEmail(email);

    return data;
  }

  async updateUser(id: string, updateAuthDto: UpdateAuthDto) {
    const user = await this.userRepository.findOne(id);

    if (isEmpty(user)) {
      throw new NotFoundException('login.user_not_found');
    } else {
      if (updateAuthDto.first_name) {
        user.first_name = updateAuthDto.first_name;
      }

      if (updateAuthDto.last_name !== undefined) {
        user.last_name = updateAuthDto.last_name;
      }

      if (updateAuthDto.contact_no) {
        user.contact_no = updateAuthDto.contact_no;
      }
      if (updateAuthDto.profile_picture) {
        user.profile_picture = updateAuthDto.profile_picture;
      }

      await this.user.update(id, updateAuthDto);
    }
  }

  async logout(id: string): Promise<void> {
    if (!isValidUuidV4(id)) {
      throw new UnauthorizedException(lan('login.user_not_found'));
    }

    const user = await this.userRepository.findUserId(id);

    if (isEmpty(user)) {
      throw new UnauthorizedException(lan('login.user_not_found'));
    }

    // Set the access_token and access_token_expiry to null
    user.access_token = null;
    user.access_token_expiry = null;
    await this.user.save(user);
  }

  async findUser(id: string) {
    return await this.userRepository.findOne(id);
  }
}
