import { Injectable, Logger } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from 'src/prisma/prisma.service';

import * as bcrypt from 'bcrypt';
import { EmailsService } from 'src/emails/emails.service';
import { randomUUID } from 'crypto';

const roundsOfHashing = parseInt(process.env.ROUNDS_OF_HASHING ? process.env.ROUNDS_OF_HASHING : '12');

@Injectable()
export class UsersService {
	constructor(
		private prisma: PrismaService,
		private emailer: EmailsService
	) {}
	private logger = new Logger(UsersService.name);

	async create(createUserDto: CreateUserDto, emailUser: boolean = true) {
		const hashedPassword = await bcrypt.hash(createUserDto.password, roundsOfHashing);

		createUserDto.password = hashedPassword;

		const transaction = await this.prisma.$transaction(async (tx) => {
			const userReturn = await tx.user.create({ data: createUserDto });
			this.logger.log({ level: 'info', message: `User created with ${createUserDto}`, refCode: '12201' });
			if (emailUser) {
				this.emailer.newUserEmail(createUserDto.email, createUserDto.name, userReturn.uuid);
			}
			return userReturn;
		});

		return transaction;
	}

	findAll(skip = 0, take = 10, sortBy: string, sortOrder: string, search: string = '', active: string | undefined = undefined, clientId: number | undefined = undefined, role: string | Array<string> | undefined = []) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: 'Returning all users', refCode: '12202' });

			if (take < 0) {
				take = 100;
			}
			let searchID = parseInt(search);

			if (Number.isNaN(searchID)) {
				searchID = 0;
			}

			// Default Query
			let query = {
				skip,
				take,
				orderBy: {
					[sortBy]: sortOrder,
				},
				include: {
					client: true,
				},
				where: {
					role: {
						not: 'SUPER_ADMIN', // Exclude the Super Admin role
					},
				},
			} as any;

			// Add Search Query to Where
			if (search) {
				query.where.OR = [{ id: { equals: searchID } }, { name: { contains: search } }, { email: { contains: search } }, { client: { name: { contains: search } } }];
			}

			// Add Active Query to Where
			if (active !== undefined) {
				query.where.active = active === 'true' ? true : false;
			}

			// Add Client ID Query to Where
			if (clientId) {
				query.where.clientId = clientId;
			}

			// Add Role Query to Where
			if (role.length > 0) {
				query.where.role = { in: role };
			}

			// Query
			return tx.user.findManyAndCount(query);

			// return tx.user.findMany({ where: { active: true } });
		});
		return transaction;
	}

	findOne(id: number) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning one user ${id}`, refCode: '12202' });
			return tx.user.findUnique({
				include: {
					client: true,
				},
				where: { id, active: true },
			});
		});
		return transaction;
	}

	findClientUsers(clientId: number, activeUsersOnly: boolean) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning all users for client ${clientId}`, refCode: '12202' });
			let query = activeUsersOnly ? { where: { clientId, active: true } } : { where: { clientId } };
			return tx.user.findMany(query);
		});
		return transaction;
	}

	findUsersByName(name: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning all users with name ${name}`, refCode: '12202' });
			return tx.user.findMany({ where: { name, active: true } });
		});
		return transaction;
	}

	update(id: number, updateUserDto: UpdateUserDto) {
		const transaction = this.prisma.$transaction(async (tx) => {
			if (updateUserDto.password) {
				updateUserDto.password = await bcrypt.hash(updateUserDto.password, roundsOfHashing);
			}

			this.logger.log({ level: 'info', message: `Updating one user ${id} with ${JSON.stringify(updateUserDto)}`, refCode: '12203' });
			return tx.user.update({
				where: { id },
				data: updateUserDto,
			});
		});
		return transaction;
	}

	remove(id: number) {
		const transaction = this.prisma.$transaction(async (tx) => {
			this.logger.log({ level: 'info', message: `Deactivating one user ${id}`, refCode: '12204' });
			return tx.user.update({
				where: { id, role: { not: 'SUPER_ADMIN' } }, // Prevent deactivation of Super Admin
				data: { active: false },
			});
		});

		return transaction;
	}

	findClientUsersSimple(clientId: number, activeUsersOnly: boolean = true) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning all users for client ${clientId} simply`, refCode: '12202' });
			let whereQuery = activeUsersOnly ? { clientId, active: true } : { clientId };
			return tx.user.findMany({ where: whereQuery, select: { id: true, name: true } });
		});
		return transaction;
	}

	findUsersByNameSimple(name: string, activeUsersOnly: boolean = true) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning all users with name ${name} simply`, refCode: '12202' });
			const whereQuery = activeUsersOnly ? { name, active: true } : { name };
			return tx.user.findMany({ where: whereQuery, select: { id: true, name: true } });
		});
		return transaction;
	}

	findUsersByEmailSimple(email: string, activeUsersOnly: boolean = true) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning all users with name ${email} simply`, refCode: '12202' });
			const whereQuery = activeUsersOnly ? { email, active: true } : { email };
			return tx.user.findMany({ where: whereQuery, select: { id: true, email: true } });
		});
		return transaction;
	}

	findAllSimple(params: any) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: 'Returning all users simply', refCode: '12202' });

			let whereQuery = {};

			if (params.activeOnly) {
				whereQuery = { active: true };
			}

			if (params.clientId) {
				whereQuery = { ...whereQuery, clientId: parseInt(params.clientId) };
			}

			if (params.name) {
				whereQuery = { ...whereQuery, name: { contains: params.name } };
			}

			if (params.email) {
				whereQuery = { ...whereQuery, email: params.email };
			}

			if (params.role) {
				const role = params.role === 'USER' ? ['SUPER_ADMIN', 'ADMIN', 'USER'] : [params.role];
				whereQuery = { ...whereQuery, role: { in: role } };
			}

			return tx.user.findMany({ where: whereQuery, select: { id: true, name: true, active: true, role: true } });
		});
		return transaction;
	}

	activateUser(cuid: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			this.logger.log({ level: 'info', message: `Activating user ${cuid}`, refCode: '12205' });
			const user = tx.user.update({
				where: { uuid: cuid },
				data: { active: true },
			});

			return user;
		});
		return transaction;
	}

	keyCheckUser(cuid: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: `Returning user ${cuid}`, refCode: '12205' });
			const user = tx.user.findUnique({ where: { uuid: cuid } });
			return user;
		});
		return transaction;
	}

	resetPassword(email: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			this.logger.log({ level: 'info', message: `Resetting password for ${email}`, refCode: '12206' });
			const user = await tx.user.findUnique({ where: { email } });
			if (user !== null) {
				const link = randomUUID();
				await tx.user.update({ where: { email }, data: { uuid: link } });
				this.emailer.passwordResetEmail(email, link);
			}
			return { message: 'success' };
		});
		return transaction;
	}

	updatePassword(key: string, body: any) {
		const transaction = this.prisma.$transaction(async (tx) => {
			const hashedPassword = await bcrypt.hash(body.password, roundsOfHashing);

			this.logger.log({ level: 'info', message: `Updating one user ${key} with new password`, refCode: '12203' });
			return tx.user.update({
				where: { uuid: key },
				data: { password: hashedPassword, uuid: randomUUID() },
			});
		});
		return transaction;
	}
}
