import BaseService from './BaseService.js';
import Friendship from '../models/community/friendship.js';
import User from '../models/community/user.js';
import { Op } from 'sequelize';
import UserParam from '../models/community/user_param.js';
import UserParamType from '../models/type/user_param.js';
import UserParamValue from '../models/type/user_param_value.js';
import { notifyUser } from '../utils/socket.js'; 

class FriendshipService extends BaseService {
    genders = {};

    async endFriendship(hashedUserId, friendUserId) {
        const user = await this.getUserByHashedId(hashedUserId);
        const friend = await this.getUserByHashedId(friendUserId);
        if (!user) throw new Error('User not found.');

        const friendship = await Friendship.findOne({
            where: {
                [Op.or]: [
                    { user1Id: user.id, user2Id: friend.id },
                    { user1Id: friend.id, user2Id: user.id },
                ],
            },
        });

        if (!friendship) throw new Error('Friendship not found.');
        await friendship.destroy();
        notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
    }

    async acceptFriendship(hashedUserId, friendUserId) {
        const user = await this.getUserByHashedId(hashedUserId);
        const friend = await this.getUserByHashedId(friendUserId);
        if (!user) throw new Error('User not found.');
        const friendship = await Friendship.findOne({
            where: { user1Id: friend.id, user2Id: user.id, accepted: false },
        });

        if (!friendship) throw new Error('Cannot accept this friendship.');
        friendship.accepted = true;
        await friendship.save();
        notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
    }

    async rejectFriendship(hashedUserId, friendUserId) {
        const user = await this.getUserByHashedId(hashedUserId);
        const friend = await this.getUserByHashedId(friendUserId);
        if (!user) throw new Error('User not found.');
        const friendship = await Friendship.findOne({
            where: { user1Id: friend.id, user2Id: user.id, accepted: false },
        });

        if (!friendship) throw new Error('Cannot reject this friendship.');
        friendship.denied = true;
        await friendship.save();
        notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
    }

    async withdrawRequest(hashedUserId, friendUserId) {
        const user = await this.getUserByHashedId(hashedUserId);
        const friend = await this.getUserByHashedId(friendUserId);
        if (!user) throw new Error('User not found.');

        const friendship = await Friendship.findOne({
            where: { user1Id: user.id, user2Id: friend.id, accepted: false },
        });

        if (!friendship) throw new Error('Cannot withdraw this request.');
        await friendship.destroy();
        notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
    }


    async getFriendships(hashedUserId, acceptedOnly) {
        const user = await this.getUserByHashedId(hashedUserId);
        if (!user) throw new Error('User not found.');

        const whereCondition = acceptedOnly
            ? { accepted: true, withdrawn: false, denied: false }
            : {};

        const friendships = await Friendship.findAll({
            where: {
                ...whereCondition,
                [Op.or]: [
                    { user1Id: user.id },
                    { user2Id: user.id },
                ],
            },
            include: [
                {
                    model: User,
                    as: 'friendSender',
                    attributes: ['username', 'hashedId'],
                    include: [
                        {
                            model: UserParam,
                            as: 'user_params',
                            required: false,
                            include: [
                                {
                                    model: UserParamType,
                                    as: 'paramType',
                                    required: true,
                                    where: { description: 'gender' },
                                    attributes: ['description'],
                                },
                            ],
                            attributes: ['value'],
                        },
                    ],
                },
                {
                    model: User,
                    as: 'friendReceiver',
                    attributes: ['username', 'hashedId'],
                    include: [
                        {
                            model: UserParam,
                            as: 'user_params',
                            required: false,
                            include: [
                                {
                                    model: UserParamType,
                                    as: 'paramType',
                                    required: true,
                                    where: { description: 'gender' },
                                    attributes: ['description'],
                                },
                            ],
                            attributes: ['value'],
                        },
                    ],
                },
            ],
        });

        const processedFriendships = await Promise.all(
            friendships.map(async (friendship) => {
                const isInitiator = friendship.user1Id === user.id;
                const otherUser = isInitiator ? friendship.friendReceiver : friendship.friendSender;
                const genderParam = otherUser.user_params?.find(param => param.paramType.description === 'gender');
                const gender = genderParam ? await this.getGender(genderParam) : null;

                return {
                    id: friendship.id,
                    user: {
                        username: otherUser.username,
                        hashedId: otherUser.hashedId,
                        gender,
                    },
                    accepted: friendship.accepted,
                    denied: friendship.denied,
                    withdrawn: friendship.withdrawn,
                    isInitiator,
                };
            })
        );

        return processedFriendships;
    }

    async getGender(genderParam) {
        if (!this.genders) {
            this.genders = {};
        }
        if (this.genders[genderParam.value]) return this.genders[genderParam.value];
        const genderObject = await UserParamValue.findOne({
            where: { id: genderParam.value },
        });
        if (genderObject) {
            this.genders[genderParam.value] = genderObject.value;
            return genderObject.value;
        }
        return '';
    }
}

export default new FriendshipService();
