import { HttpException, HttpStatus, Injectable, NotFoundException,  } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Repository } from 'typeorm';
import { CreateUsersDto } from './dto/create-users.dto';
import { UpdateUsersDto } from './dto/update-users.dto';
import { Friendship } from '../friendship/entities/friendship.entity';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { UserStats } from './entities/userStat.entities';
import { FriendshipService } from 'src/friendship/friendship.service';
import { stringify } from 'querystring';

// 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(id: string) {
		console.log(`FIND ONE USER SERVICE Find user ${id}`);
		const user = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.id = :id', { id: +id })
		.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,
		};
		console.log(`Returned Partial User.` + partialUser.username + user.username);
		return partialUser;
	}

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

	async findOneByUsername(userConnectedId : string, usernameToFind: string) {
		const userToFind : User  = await this.userRepository.createQueryBuilder('user')
		.leftJoinAndSelect('user.stats', 'stats')
		.where('user.username = :username', { username: usernameToFind })
		.getOne();
		console.log('USERNAME OF FOUND USER : ' + userToFind.username);
		// console.log({...user})
		// you can't do that, you need to do user === undefined
		// if (user === undefined)
		if (!userToFind)
			throw new HttpException(`The user could not be found 1.`,HttpStatus.NOT_FOUND);
		if (await this.friendshipService.findIfUserIsBlockedOrHasBlocked(userConnectedId, userToFind.id.toString())) {
			console.log('we are blocked in user.service')
			throw new HttpException(`The user could not be found 2.`,HttpStatus.NOT_FOUND);
		}
		const partialUser : Partial<User> = {
			username: userToFind.username,
			image_url: userToFind.image_url,
			status: userToFind.status,
			stats: userToFind.stats,
		};
		return partialUser;
	}

	// Example : http://localhost:3000/users?limit=10&offset=20
	findAll(paginationquery : PaginationQueryDto) {
		const { limit, offset } = paginationquery;
		return this.userRepository.find({
			skip: offset,
			take: limit,
		});
	}

	async create(createUserDto: CreateUsersDto) {
		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: string, updateUserDto: UpdateUsersDto) {
		console.log(`Update user ${id} with ${updateUserDto.isEnabledTwoFactorAuth}`);
		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: string) {
		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: string) {
		return this.userRepository.update(id, {isEnabledTwoFactorAuth: true});
	}

	async authenticateUserWith2FA(id: string) {
		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});
	}

	async getAvatarUrl(id: number) {
		const user = await this.userRepository.findOneBy({id: id});
		if (!user)
			throw new HttpException(`The user could not be found.`,HttpStatus.NOT_FOUND);
		if (!user.image_url)
			throw new HttpException(`The user has no avatar.`,HttpStatus.NOT_FOUND);
			console.log(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.winGame++;
		user.stats.totalGame++;
		this.userRepository.save(user);
	}
}
