const dbSAPPS = require('../../helpers/db');
require('dotenv').config();
const { Op } = require('sequelize');

module.exports = {
    filter,
    assignProjectsToUser,
    getAllUserProjects,
    getUserIdsByProjectIds,
    deleteUserProjects
};

async function getProjectIdsByName(names) {
    const projects = await dbSAPPS.Project.findAll({
        where: {
            name: names
        },
        attributes: ['id']
    });

    return projects.map(project => project.id);
}

async function filter(params) {
    const filterConditions = {};

    if (params.user_id) {
        filterConditions.user_id = {
            [Op.eq]: `${params.user_id}`,
        };
    }

    try {
        const userProjects = await dbSAPPS.UserProject.findAll({
            include: [
                {
                    model: dbSAPPS.User,
                    as: 'user',
                    attributes: ['id', 'email', 'username', 'full_name']
                },
                {
                    model: dbSAPPS.Project,
                    as: 'project',
                    attributes: ['id', 'name', 'display_name', 'slug_name']
                }
            ],
            where: filterConditions,
            order: [['updatedAt', 'DESC']],
        });

        const result = userProjects.reduce((acc, userProject) => {
            const id = userProject.user.id;
            const user_id = userProject.user.id;
            const email = userProject.user.email;
            const username = userProject.user.username;
            const fullName = userProject.user.full_name;
            const projectName = userProject.project.name;

            const createdAt = userProject.createdAt;
            const updatedAt = userProject.updatedAt;

            let existingUser = acc.find(item => item.user_id === user_id);
            if (!existingUser) {
                existingUser = {
                    id,
                    user_id,
                    email,
                    username,
                    fullName,
                    projects: [],
                    projectsObject: {},
                    createdAt,
                    updatedAt
                };
                acc.push(existingUser);
            }

            existingUser.projects.push(projectName);
            existingUser.projectsObject = existingUser.projects.reduce((acc, name) => {
                acc[name] = true;
                return acc;
            }, {});

            return acc;
        }, []);

        return {
            userProjects: result,
            success: true,
            message: 'User project has been loaded'
        };
    } catch (error) {
        console.error('Error retrieving user project: ', error);
        const errorMessage = error.message || 'Failed to retrieve user project. Please try again later.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function assignProjectsToUser(params) {
    const transaction = await dbSAPPS.sequelize.transaction();
    try {
        const { userId, lastUsedUserId, projectNames, update } = params;
        if (!userId || !Array.isArray(projectNames) || projectNames.length === 0) {
            return {
                success: false,
                message: 'Invalid input. Please ensure that all required fields are provided in the correct format.'
            };
        }

        const user = await dbSAPPS.User.findByPk(userId, { transaction });
        if (!user) {
            await transaction.rollback();
            return { success: false, message: 'User not found!' };
        }

        const userProjects = await dbSAPPS.UserProject.findOne({ where: { user_id: userId } });
        if (userProjects && update === false) {
            await transaction.rollback();
            return { success: false, message: 'User is already assigned to a project, please update the assignment if necessary.' };
        }

        if (lastUsedUserId && update === true) {
            const lastUse = await dbSAPPS.UserProject.findOne({ where: { user_id: lastUsedUserId } });
            if (lastUse) {
                await lastUse.destroy({ transaction });
            }
        }

        const projectIds = await getProjectIdsByName(projectNames);

        await user.setProjects(projectIds, { transaction });
        await transaction.commit();
        return {
            success: true,
            message: 'Projects have been assigned to the user!'
        };
    } catch (error) {
        await transaction.rollback();
        const errorMessage = error.message || 'Failed to assign projects to the user.';
        return { success: false, message: errorMessage };
    }
}

async function getAllUserProjects() {
    try {
        const userProjects = await dbSAPPS.UserProject.findAll({
            include: [
                {
                    model: dbSAPPS.User,
                    as: 'user',
                    attributes: ['id', 'email', 'username', 'full_name']
                },
                {
                    model: dbSAPPS.Project,
                    as: 'project',
                    attributes: ['id', 'name', 'display_name', 'slug_name']
                }
            ],
            order: [['updatedAt', 'DESC']],
        });


        const result = userProjects.reduce((acc, userProject) => {
            const id = userProject.user.id;
            const user_id = userProject.user.id;
            const email = userProject.user.email;
            const username = userProject.user.username;
            const fullName = userProject.user.full_name;
            const projectName = userProject.project.name;

            const createdAt = userProject.createdAt;
            const updatedAt = userProject.updatedAt;

            let existingUser = acc.find(item => item.user_id === user_id);
            if (!existingUser) {
                existingUser = {
                    id,
                    user_id,
                    email,
                    username,
                    fullName,
                    projects: [],
                    projectsObject: {},
                    createdAt,
                    updatedAt
                };
                acc.push(existingUser);
            }

            existingUser.projects.push(projectName);
            existingUser.projectsObject = existingUser.projects.reduce((acc, name) => {
                acc[name] = true;
                return acc;
            }, {});

            return acc;
        }, []);

        return {
            userProjects: result,
            success: true,
            message: 'User projects has been loaded'
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to load user projects!';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function getUserIdsByProjectIds(params) {
    try {
        if (!Array.isArray(params.projectIds)) {
            params.projectIds = [params.projectIds];
        }

        const filterConditions = {};

        if (params.projectIds) {
            filterConditions.project_id = {
                [Op.in]: params.projectIds
            };
        }

        const userProjects = await dbSAPPS.UserProject.findAll({
            where: filterConditions,
            attributes: ['user_id'],
        });

        if (!userProjects || userProjects.length === 0) {
            return {
                success: false,
                message: 'No users found for the provided project IDs.',
                userIds: []
            };
        }

        const userIds = userProjects.map(userProject => userProject.user_id);

        return {
            success: true,
            message: 'User IDs retrieved successfully!',
            userIds: [...new Set(userIds)]
        };
    } catch (error) {
        console.error('Error retrieving user IDs by project ID:', error);
        return {
            success: false,
            message: 'An error occurred while retrieving user IDs. Please try again later.',
            error: error.message
        };
    }
}

async function deleteUserProjects(id) {
    const transaction = await dbSAPPS.sequelize.transaction();
    try {
        const user = await dbSAPPS.User.findByPk(id, { transaction });
        if (!user) {
            await transaction.rollback();
            return { success: false, message: 'User not found!' };
        }

        const userProjects = await dbSAPPS.UserProject.findAll({
            where: { user_id: id },
            transaction
        });

        // debug
        // console.log('userProjects', userProjects)

        if (userProjects.length === 0) {
            await transaction.rollback();
            return {
                success: false,
                message: 'User has no associated projects, so it was not deleted.'
            };
        }

        await dbSAPPS.UserProject.destroy({
            where: { user_id: id },
            transaction
        });

        await transaction.commit();
        return {
            success: true,
            message: 'User and projects has been deleted!'
        };
    } catch (error) {
        await transaction.rollback();
        const errorMessage = error.message || 'Failed to delete user and projects.';
        return { success: false, message: errorMessage };
    }
}
