import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { Repository } from 'typeorm';
import { CreateFriendshipDto } from './dto/create-friendship.dto';
import { Friendship, FriendshipStatus } from './entities/friendship.entity';

@Injectable()
export class FriendshipService {

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


	async findOneFriend(friendshipId: string, username: string) {
		const friendship = await this.friendshipRepository.find({ where: { id: +friendshipId, senderUsername: username, status: FriendshipStatus.ACCEPTED } });
		if (!friendship)
			throw new HttpException(`The requested friend not found.`, HttpStatus.NOT_FOUND);
		return friendship;
	}

	async findOneBlocked(friendshipId: string, username: string) {
		const friendship = await this.friendshipRepository.find({ where: { id: +friendshipId, senderUsername: username, status: FriendshipStatus.BLOCKED } });
		if (!friendship)
			throw new HttpException(`The requested blocked not found.`, HttpStatus.NOT_FOUND);
		return friendship;
	}

	async findAllFriends(username: string) {
		const friendship = await this.friendshipRepository
			.createQueryBuilder('friendship')
			.where('friendship.status = :status', { status: FriendshipStatus.ACCEPTED })
			.andWhere('friendship.receiverUsername = :addressee', { addressee: username })
			.orWhere('friendship.senderUsername = :requester', { requester: username })
			.andWhere('friendship.status = :status', { status: FriendshipStatus.ACCEPTED })
			.getMany();
		for (const friend of friendship)
			console.log("FRIENDSHIP : " + friend.status);
		return friendship;
	}

	async findAllBlockedFriends(username: string) {
		const friendships : Friendship[] =  await this.friendshipRepository
			.createQueryBuilder('friendship')
			.where('friendship.senderUsername = :requestee', { requestee: username })
			.andWhere('friendship.status = :status', { status: FriendshipStatus.BLOCKED })
			.getMany();
		let partialFriendship : Partial<Friendship>[] = [];
		for (const friendship of friendships) {
			partialFriendship.push({id: friendship.id, date: friendship.date, senderUsername: friendship.senderUsername, receiverUsername: friendship.receiverUsername, status: friendship.status});
		}
		return partialFriendship;
	}

	async findAllPendantRequestsForFriendship(username: string) {
		const friendship =  await this.friendshipRepository
			.createQueryBuilder('friendship')
			.where('friendship.senderUsername = :requestee', { requestee: username })
			.andWhere('friendship.status = :status', { status: FriendshipStatus.REQUESTED })
			.getMany();
		let partialFriendship : Partial<Friendship>[] = [];
		for (const friend of friendship) {
			console.log("FRIENDSHIP : " + friend);
			partialFriendship.push({id: friend.id, senderUsername: friend.senderUsername, receiverUsername: friend.receiverUsername, status: friend.status});
		}
		console.log("Pendant requests : " + partialFriendship);
		return partialFriendship;
	}

	async findAllReceivedRequestsForFriendship(username: string) {
		const friendship = await this.friendshipRepository
			.createQueryBuilder('friendship')
			.where('friendship.receiverUsername = :addressee', { addressee: username })
			.andWhere('friendship.status = :status', { status: FriendshipStatus.REQUESTED })
			.getMany();
		let partialFriendship : Partial<Friendship>[] = [];
		for (const friend of friendship) {
			partialFriendship.push({id: friend.id, senderUsername: friend.senderUsername, receiverUsername: friend.receiverUsername, status: friend.status});
		}
		return partialFriendship;
	}

	async create(createFriendshipDto: CreateFriendshipDto, creator : User) : Promise <Partial<Friendship>> {
		console.log("DTO : \n")
		console.log({...createFriendshipDto})
		const receiver = await this.userRepository.findOneBy({username: createFriendshipDto.receiverUsername});
		if (!receiver)
			throw new HttpException(`The addressee does not exist.`, HttpStatus.NOT_FOUND);
		if (createFriendshipDto.status !== FriendshipStatus.REQUESTED && createFriendshipDto.status !== FriendshipStatus.BLOCKED)
			throw new HttpException(`The status is not valid.`, HttpStatus.NOT_FOUND);
		const friendship = await this.friendshipRepository.findOneBy({ sender: creator, receiver: receiver });
		if (friendship) {
			if (friendship.status && friendship.status === FriendshipStatus.ACCEPTED)
				throw new HttpException(`The friendship request has already been accepted.`, HttpStatus.OK);
			else if (friendship.status && friendship.status === FriendshipStatus.REQUESTED)
				throw new HttpException(`The friendship request has already been sent the ${friendship.date}.`, HttpStatus.OK);
			else if (friendship.status && friendship.status === FriendshipStatus.BLOCKED)
				throw new HttpException(`We can't do that`, HttpStatus.OK);
			else if (friendship.status && friendship.status === FriendshipStatus.DECLINED)
				throw new HttpException(`The request has been declined.`, HttpStatus.OK);
		}
		const newFriendship = new Friendship();
		newFriendship.sender = creator;
		newFriendship.senderUsername = creator.username;
		newFriendship.receiver = receiver;
		newFriendship.receiverUsername = receiver.username;
		newFriendship.status = createFriendshipDto.status;
		const savedFriendship = this.friendshipRepository.save(newFriendship);
		const partialFriendship : Partial<Friendship> = {
			id : (await savedFriendship).id,
			date : (await savedFriendship).date,
			receiverUsername: (await savedFriendship).receiverUsername,
			status : (await savedFriendship).status
		}
		console.log({...partialFriendship})
		return partialFriendship;
	}

	async acceptFriendship(relationshipId: string, user: User) {
		const relation = await this.friendshipRepository.findOneBy({ id: +relationshipId });
		if (!relation)
			throw new HttpException(`The requested relationship not found.`, HttpStatus.NOT_FOUND);
		if (relation.sender.id === user.id) {
			throw new HttpException(`You can't accept your own request.`, HttpStatus.NOT_FOUND);
		}
		relation.status = FriendshipStatus.ACCEPTED;
		const savedFriendship =  this.friendshipRepository.save(relation);
		const partialFriendship : Partial<Friendship> = {
			id : (await savedFriendship).id,
			date : (await savedFriendship).date,
			receiverUsername: (await savedFriendship).receiverUsername,
			status : (await savedFriendship).status
		}
		return partialFriendship;
	}

	async declineFriendship(relationshipId: string, user: User) {
		const relation = await this.friendshipRepository.findOneBy({ id: +relationshipId });
		if (!relation)
			throw new HttpException(`The requested relationship not found.`, HttpStatus.NOT_FOUND);
		if (relation.sender.id === user.id) {
			throw new HttpException(`You can't accept your own request.`, HttpStatus.NOT_FOUND);
		}
		relation.status = FriendshipStatus.DECLINED;
		const savedFriendship =  this.friendshipRepository.save(relation);
		const partialFriendship : Partial<Friendship> = {
			id : (await savedFriendship).id,
			date : (await savedFriendship).date,
			receiverUsername: (await savedFriendship).receiverUsername,
			status : (await savedFriendship).status
		}
		return partialFriendship
	}

	async blockFriendship(relationshipId: string, user: User) {
		const relation = await this.friendshipRepository.findOneBy({ id: +relationshipId });
		if (!relation)
			throw new HttpException(`The requested relationship not found.`, HttpStatus.NOT_FOUND);
		if (relation.sender.id === user.id) {
			throw new HttpException(`You can't accept your own request.`, HttpStatus.NOT_FOUND);
		}
		relation.status = FriendshipStatus.BLOCKED;
		const savedFriendship =  this.friendshipRepository.save(relation);
		const partialFriendship : Partial<Friendship> = {
			id : (await savedFriendship).id,
			date : (await savedFriendship).date,
			receiverUsername: (await savedFriendship).receiverUsername,
			status : (await savedFriendship).status
		}
		return partialFriendship
	}

	async removeFriendship(relationshipId: string, user : User) {
		const friendship = await this.friendshipRepository.findOneBy({ id: +relationshipId });
		if (!friendship)
			throw new HttpException(`Your friend could not be deleted.`, HttpStatus.NOT_FOUND);
		if (friendship.sender.id !== user.id || friendship.receiver.id !== user.id) {
				throw new HttpException(`You can't do that.`, HttpStatus.FORBIDDEN);
			}
		return this.friendshipRepository.remove(friendship);
	}

	async findIfUserIsBlockedOrHasBlocked(userConnectedId: string, userToFindId: string) {
		console.log("finding if user is blocked")
		const friendship = await this.friendshipRepository
			.createQueryBuilder('friendship')
			.where('friendship.senderUsername = :requestee', { requestee: userConnectedId })
			.orWhere('friendship.senderUsername = :requesteeBis', { requesteeBis: userToFindId })
			.andWhere('friendship.status = :status', { status: FriendshipStatus.BLOCKED })
			.getOne();
		if (friendship) {
			console.log('we are blocked in friendship.service')
			return true;
		}
		return false;
	}
}
