/* eslint-disable import/no-extraneous-dependencies */
import { faker } from '@faker-js/faker';
import generator from 'generate-password';

import crypto from 'crypto';
import DBClient from '../../DBClient';
import { hashPassword } from '../../../config/auth/passwordFns';
import logger from '../../../config/pino/logger';

interface CreateNewUsersArgs {
  numberOfUsers: number;
}

interface UserData {
  firstName: string;
  lastName: string;
  email: string;
  username: string;
  dateOfBirth: Date;
  createdAt: Date;
  hash: string;
  accountIsVerified: boolean;
  role: 'USER' | 'ADMIN';
  bio: string;
}

const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => {
  const prisma = DBClient.instance;
  await DBClient.instance.$disconnect();

  const passwords = Array.from({ length: numberOfUsers }, () =>
    generator.generate({
      length: 20,
      symbols: true,
      numbers: true,
      uppercase: true,
      strict: true,
    }),
  );

  logger.info('Hashing passwords. This may take a while...');
  const hashedPasswords = await Promise.all(
    passwords.map((password) => hashPassword(password)),
  );

  logger.info('Creating new users. This may take a while...');

  const data: UserData[] = [];

  const takenUsernames: string[] = [];
  const takenEmails: string[] = [];

  // eslint-disable-next-line no-plusplus
  for (let i = 0; i < numberOfUsers; i++) {
    const randomValue = crypto.randomBytes(1).toString('hex');
    const firstName = faker.person.firstName();
    const lastName = faker.person.lastName();
    const username = `${firstName[0]}.${lastName}.${randomValue}`.toLowerCase();
    const email = faker.internet
      .email({ firstName, lastName, provider: 'example.com' })
      .toLowerCase();

    const hash = hashedPasswords[i];
    const userAvailable =
      !takenUsernames.includes(username) && !takenEmails.includes(email);

    if (!userAvailable) {
      i -= 1;

      // eslint-disable-next-line no-continue
      continue;
    }
    takenUsernames.push(username);
    takenEmails.push(email);

    const dateOfBirth = faker.date.birthdate({ mode: 'age', min: 19 });
    const createdAt = faker.date.past({ years: 4 });
    const bio = faker.lorem.paragraphs(1).replace(/\n/g, ' ');

    const user: UserData = {
      firstName,
      lastName,
      email,
      username,
      dateOfBirth,
      createdAt,
      hash,
      bio,
      accountIsVerified: true,
      role: 'USER',
    };

    data.push(user);
  }

  await prisma.user.createMany({ data, skipDuplicates: true });
  return prisma.user.findMany({
    where: { role: { not: 'ADMIN' } },
  });
};

export default createNewUsers;
