import { HttpException, HttpStatus, Injectable, NotFoundException,  } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Repository, Not } from 'typeorm';
import { CreateUsersDto } from './dto/create-users.dto';
import { UpdateUsersDto } from './dto/update-users.dto';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { UserStats } from './entities/userStat.entities';
import { FriendshipService } from 'src/friendship/friendship.service';
// On va devoir sûrement trouver un moyen plus simple pour passer l'id, sûrement via des pipes
// ou des interceptors, mais pour l'instant on va faire comme ça.
@Injectable()
export class UsersService {

	constructor(
	private readonly friendshipService: FriendshipService,
	@InjectRepository(User)
	private readonly userRepository: Repository<User>,
	) {}

	async findOneByFourtyTwoId(fortytwo_id: string) {
		const user  = await this.userRepository.findOneBy({fortyTwoId: fortytwo_id});
		if (!user)
		{
			console.log(`The requested user not found.`);
			return null;
		}
		console.log(`The requested user found.`);
		return user;
	}

	async findOne(username: string) {
		console.log(`FIND ONE USER SERVICE Find user ${username}`);
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.username = :username', { username: username })
		.getOne();
		if (!user)
			throw new NotFoundException(`The requested user not found.`);
		console.log(`FIND ONE USER SERVICE The requested user found. ` + user.username + " "
		+ user.stats.id + user.stats.winGame + user.stats.loseGame + user.stats.drawGame + user.stats.totalGame);
		const partialUser : Partial<User> = {
			username: user.username,
			image_url: user.image_url,
			isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth,
			status: user.status,
			stats: user.stats,
			currentRoom: user.currentRoom,
		};
		console.log(`Returned Partial User.` + partialUser.username + user.username);
		return partialUser;
	}

	async isUsernameExists(usernameToSearch: string): Promise<boolean> {
		console.log('searching for username: ' + usernameToSearch)
		const user = await this.userRepository.findOneBy({username : usernameToSearch});
		console.log({...user})
		if (!user)
			return false;
		return true;
	}

	async findAll(currentUser: User) {
		// const otherUsers = await this.userRepository.find({where: {id: Not(+currentUser.id)}, order: {username: "ASC"}, skip: offset, take: limit,});
		const otherUsers = await this.userRepository.find({where: {id: Not(+currentUser.id)}, order: {username: "ASC"}});

		let partialUsers : Partial<User>[] = [];

		for (const otherUser of otherUsers) {
			// console.log('other user: ')
			// console.log({...otherUser})
			// let tmp = await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id);
			// console.log('user.services findIF Blocked... : ')
			// console.log(tmp)
			// if (tmp === false) {
			if (await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id) === false) {
				partialUsers.push({username: otherUser.username, image_url: otherUser.image_url, status: otherUser.status, stats: otherUser.stats});
			}
		}
		console.log('user.services findAll, partialUsers:')
		console.log({...partialUsers})
		return partialUsers;
	}

	async create(createUserDto: CreateUsersDto) {
		// console.log('\nuser.services create a new user, createUserDto: ')
		// console.log({...createUserDto})
		if (await this.userRepository.findOneBy({fortyTwoId: createUserDto.fortyTwoId}))
			throw new HttpException(`The user already exists.`,HttpStatus.CONFLICT);
		const user = this.userRepository.create(createUserDto);
		if (!user)
			throw new HttpException(`The user could not be created.`,HttpStatus.NOT_FOUND);
		user.stats = new UserStats();
		return this.userRepository.save(user);
	}

	async update(id: number, updateUserDto: UpdateUsersDto) {
		// console.log(`Update user ${id} with ${updateUserDto.isEnabledTwoFactorAuth}`);
		// console.log({...updateUserDto})
		if (await this.isUsernameExists(updateUserDto.username) === true) {
			console.log('updating username ' + updateUserDto.username + ' but it already is in use')
			throw new HttpException(`The username is already in use.`,HttpStatus.CONFLICT);
		}
		const user = await this.userRepository.preload(
			{id: id,
			...updateUserDto});
		if (!user)
			throw new HttpException(`The user could not be updated.`,HttpStatus.NOT_FOUND);
		return this.userRepository.save(user);
	}

	async remove(id: number) {
		const user = await this.userRepository.findOneBy({id: id});
		if (!user)
			throw new HttpException(`The user could not be deleted.`,HttpStatus.NOT_FOUND);
		return this.userRepository.remove(user);
	}

	async enableTwoFactorAuth(id: number) {
		return this.userRepository.update(id, {isEnabledTwoFactorAuth: true});
	}

	async authenticateUserWith2FA(id: number) {
		return this.userRepository.update(id, { isTwoFactorAuthenticated: true})
	}

	async setIsTwoFactorAuthenticatedWhenLogout(id: number) {
		return this.userRepository.update(id, {isTwoFactorAuthenticated: false});
	}

	async setAuthenticatorSecret(id: number, secret: string) {
		return this.userRepository.update(id, {secretTwoFactorAuth: secret});
	}

	async updateStatus(id: number, status: string) {
		return this.userRepository.update(id, {status: status});
	}

	async updateAvatar(id: number, avatar: string) {
		return this.userRepository.update(id, {image_url: avatar});
	}

	// doing search with username not id because userService.findOne doesn't return an Id anymore, just username... fuck this architecture is big trash...
	// async getAvatarUrl(id: number) {
	async getAvatarUrl(username: string) {
		const user = await this.userRepository.findOneBy({username: username});
		if (!user)
			throw new HttpException(`The user could not be found.`,HttpStatus.NOT_FOUND);
		// console.log('user.service getAvatarUrl of ' + user.username)
		if (!user.image_url)
			throw new HttpException(`The user has no avatar.`,HttpStatus.NOT_FOUND);
		// console.log('User.service Avatar URL ' + user.image_url);
		return user.image_url;
	}

	async getStats(id: number) {
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.id = :id', { id: id })
		.getOne();
		if (!user.stats || !user)
			throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND);
		return user.stats;
	}

	async incrementVictories(id: number) {
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.id = :id', { id: id })
		.getOne();
		if (!user.stats || !user)
			throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND);
		user.stats.winGame++;
		user.stats.totalGame++;
		this.userRepository.save(user);
	}

	async incrementDefeats(id: number) {
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.id = :id', { id: id })
		.getOne();
		if (!user.stats || !user)
			throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND);
		user.stats.loseGame++;
		user.stats.totalGame++;
		this.userRepository.save(user);
	}

	async incrementDraws(id: number) {
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.id = :id', { id: id })
		.getOne();
		if (!user.stats || !user)
			throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND);
		user.stats.drawGame++;
		user.stats.totalGame++;
		this.userRepository.save(user);
	}
}
