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

module.exports = {
    create,
    getAll,
    filter,
    option,
    count,
    update,
    enabled,
    delete: _delete,
    saveUserAnswer,
    isUserAnswerExist,
    getUserAnswer,
    filterUserAnswer,
    getUserAnswerByUserId
};

async function getAll(params) {
    try {
        const filterConditions = {};

        if (params?.questionIds) {
            filterConditions.id = {
                [Op.in]: params?.questionIds
            };
        }

        const questions = await dbSAPPS.Question.findAll({
            where: filterConditions,
            order: [['modified', 'DESC']],
            include: [
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name'],
                    as: 'questionbank'
                },
                {
                    model: dbSAPPS.Answer,
                    as: 'answers'
                }
            ]
        });

        return {
            questions,
            success: true,
            message: 'Questions has been loaded!'
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to load questions!';
        return {
            success: false,
            message: errorMessage
        };
    }
}

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

    if (params?.questionIds) {
        filterConditions.id = {
            [Op.in]: params?.questionIds
        };
    }

    if (params.text) {
        filterConditions.text = {
            [Op.like]: `%${params.text}%`,
        };
    }

    if (params.enabled) {
        filterConditions.enabled = {
            [Op.eq]: params.enabled === "1" ? true : false,
        };
    }

    try {
        const questions = await dbSAPPS.Question.findAll({
            where: filterConditions,
            order: [['modified', 'DESC']],
            include: [
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name'],
                    as: 'questionbank'
                },
                {
                    model: dbSAPPS.Answer,
                    as: 'answers'
                }
            ]
        });

        return {
            questions,
            success: true,
            message: 'Questions has been loaded!'
        };
    } catch (error) {
        console.error('Error retrieving questions: ', error);
        const errorMessage = error.message || 'Failed to retrieve questions. Please try again later.';

        return {
            success: false,
            message: errorMessage
        };
    }
}

async function option(params) {
    try {
        const filterConditions = {};

        if (params?.questionIds) {
            filterConditions.id = {
                [Op.in]: params?.questionIds
            };
        }

        filterConditions.enabled = {
            [Op.eq]: true
        };

        const questions = await dbSAPPS.Question.findAll({
            attributes: ['id', 'text'],
            where: filterConditions,
            order: [['modified', 'DESC']],
        });

        return {
            questions,
            success: true,
            message: "Questions has been loaded!"
        };
    } catch (error) {
        console.error('Error retrieving questions: ', error);
        const errorMessage = error.message || 'Failed to retrieve questions. Please try again later.';

        return {
            success: false,
            message: errorMessage
        };
    }
}

async function count() {
    try {
        const count = await dbSAPPS.Question.count({
            where: { enabled: true },
        });

        return {
            count,
            success: true,
            message: "Questions has been loaded!"
        };
    } catch (error) {
        console.error('Error counting questions: ', error);
        return {
            success: false,
            message: 'Failed to retrieve count questions. Please try again later.'
        };
    }
}


async function create(params) {
    const transaction = await dbSAPPS.sequelize.transaction();
    try {
        if (!params.text) return { success: false, message: 'text are required!' };

        const existingText = await dbSAPPS.Question.findOne({ where: { text: params?.text, questionbank_id: params?.questionbank_id } });
        if (existingText) return { success: false, message: `Text "${params.text}" is already taken.` };

        if (!params.creation) params.creation = Date.now();
        if (!params.modified) params.modified = Date.now();
        if (!params.modified_by) params.modified_by = 1;
        if (!params.owner) params.owner = 1;

        const question = await dbSAPPS.Question.create(params, { transaction });

        if (params.answers) {
            params.answers = JSON.parse(params.answers);
        }

        if (Array.isArray(params.answers) && params.answers.length > 0) {
            const answerData = params.answers?.map(answer => ({
                ...answer,
                question_id: question.id
            }));
            await dbSAPPS.Answer.bulkCreate(answerData, { transaction });
        }

        await transaction.commit();
        return {
            success: true,
            message: 'Question has been created!',
        };
    } catch (error) {
        await transaction.rollback();
        const errorMessage = error.message || 'An error occurred during creating question.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function update(id, params) {
    const transaction = await dbSAPPS.sequelize.transaction();
    try {
        const question = await dbSAPPS.Question.findByPk(id);
        if (!question) return {
            success: false,
            message: 'Question not found!'
        };

        if (params.text) {
            const existingText = await dbSAPPS.Question.findOne({ where: { text: params?.text, questionbank_id: params?.questionbank_id } });
            if (existingText) {
                if (existingText.id != params.id) {
                    return {
                        success: false,
                        message: `Text "${params.text}" is already taken. Try again!`
                    };
                }
            }
        }

        await question.update(params, { transaction });

        if (params.answers) {
            params.answers = JSON.parse(params.answers);
        }

        if (Array.isArray(params.answers) && params.answers.length > 0) {
            await dbSAPPS.Answer.destroy({ where: { question_id: id }, transaction });
            const answerData = params.answers.map(answer => ({
                ...answer,
                question_id: id
            }));
            await dbSAPPS.Answer.bulkCreate(answerData, { transaction });
        } else {
            await dbSAPPS.Answer.destroy({ where: { question_id: id }, transaction });
        }

        await transaction.commit();
        return {
            success: true,
            message: 'Question has been updated!'
        };
    } catch (error) {
        await transaction.rollback();
        const errorMessage = error.message || 'Failed to update question.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function enabled(id, params) {
    try {
        const question = await dbSAPPS.Question.findByPk(id);
        if (!question) return {
            success: false,
            message: 'Question not found!'
        };

        const updateData = {};
        updateData.enabled = params.enabled;
        if (params.modified_by) updateData.modified_by = params.modified_by;
        if (params.modified) updateData.modified = params.modified;

        await question.update(updateData);
        return {
            success: true,
            message: 'Question has been updated!'
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to update question.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function _delete(id) {
    try {
        const question = await dbSAPPS.Question.findByPk(id);
        if (!question) return { success: false, message: 'Question not found!' };

        await question.destroy();
        return {
            success: true,
            message: 'Question has been deleted!'
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to delete question.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function saveUserAnswer(data) {
    try {
        const userAnswers = await dbSAPPS.UserAnswer.bulkCreate(data);
        return {
            success: true,
            message: 'User answers have been saved!',
            data: userAnswers
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to save user answers.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function isUserAnswerExist(params) {
    try {
        if (!params.user_id || !params.questionbank_id || !params.type) {
            return {
                success: false,
                message: 'User ID, Question Bank ID and type are required!'
            };
        }

        const exist = await dbSAPPS.UserAnswer.findOne({
            where: {
                user_id: parseInt(params.user_id),
                questionbank_id: parseInt(params.questionbank_id),
                type: params.type
            }
        });
        return {
            success: true,
            message: 'User answers have been saved!',
            data: exist ? true : false
        };
    } catch (error) {
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function getUserAnswer(params) {
    try {
        const userAnswers = await dbSAPPS.UserAnswer.findAll({
            order: [['updatedAt', 'DESC']],
            include: [
                {
                    model: dbSAPPS.User,
                    as: 'user',
                    attributes: ['id', 'full_name'],
                    include: [
                        {
                            model: dbSAPPS.UserChild,
                            as: 'userchilds'
                        }, {
                            model: dbSAPPS.UserAdditionalInfo,
                            as: 'useradditionalinfo'
                        }
                    ]
                },
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name', 'description'],
                    as: 'questionbank',
                    include: [
                        {
                            model: dbSAPPS.QuestionBankReview,
                            as: 'questionbankreviews',
                            attributes: ['id', 'min_point', 'max_point', 'description']
                        }
                    ]
                },
                {
                    model: dbSAPPS.Question,
                    as: 'question',
                    attributes: ['id', 'text'],
                    include: [{
                        model: dbSAPPS.Answer,
                        as: 'answers',
                        attributes: ['id', 'option', 'is_correct', 'correct_point', 'text']
                    }]
                }
            ],
            attributes: ['id', 'user_id', 'questionbank_id', 'type', 'option', 'is_correct', 'correct_point', 'question_id']
        });

        const questions = await dbSAPPS.Question.findAll({
            include: [
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name'],
                    as: 'questionbank'
                },
                {
                    model: dbSAPPS.Answer,
                    as: 'answers',
                    attributes: ['id', 'option', 'is_correct', 'correct_point']
                }
            ],
            attributes: ['id']
        });

        const questionsMap = questions.reduce((acc, question) => {
            acc[question.id] = {
                ...question,
                answers: question.answers.map(answer => ({
                    id: answer.id,
                    option: answer.option,
                    is_correct: answer.is_correct,
                    correct_point: answer.correct_point,
                    paramter: answer.parameter
                }))
            };
            return acc;
        }, {});

        const groupedUserAnswers = userAnswers.reduce((acc, userAnswer) => {
            const question = questionsMap[userAnswer.question_id];
            if (question && userAnswer.option === null) {
                userAnswer.option = question.answers.find(answer => answer.is_correct === userAnswer.is_correct)?.option || null;
            }

            const key = `${userAnswer.user_id}-${userAnswer.questionbank_id}-${userAnswer.type}`;
            if (!acc[key]) {
                acc[key] = {
                    user_id: userAnswer.user_id,
                    user: userAnswer.user,
                    questionbank_review: userAnswer.questionbank.questionbankreviews,
                    full_name: userAnswer.user.full_name,
                    questionbank_id: userAnswer.questionbank_id,
                    questionbank_display_name: userAnswer.questionbank.display_name,
                    questionbank_description: userAnswer.questionbank.description,
                    type: userAnswer.type,
                    questions: [],
                };
            }

            const questionExist = acc[key].questions.find(q => q.question.id === userAnswer.question_id);
            if (!questionExist) {
                acc[key].questions.push({
                    text: userAnswer.question.text,
                    question: userAnswer.question,
                    answer: userAnswer.option,
                    is_correct: userAnswer.is_correct,
                    correct_point: userAnswer.correct_point,
                    options: userAnswer.question.answers
                });
            }

            return acc;
        }, {});

        const groupedUserAnswersValues = Object.values(groupedUserAnswers);

        const groupedUserAnswersValuesKPSP = groupedUserAnswersValues.filter(group => group.type === 'KPSP');
        const groupedUserAnswersValuesKPSPAnswers = groupedUserAnswersValuesKPSP.reduce((acc, group) => {
            const key = `${group.user_id}-${group.questionbank_id}`;
            if (!acc[key]) {
                acc[key] = {};
            }
            for (const { answer } of group.questions) {
                if (!acc[key][answer]) {
                    acc[key][answer] = 0;
                }
                acc[key][answer]++;
            }
            return acc;
        }, {});

        for (const group of groupedUserAnswersValuesKPSP) {
            group.answerKPSP = groupedUserAnswersValuesKPSPAnswers[`${group.user_id}-${group.questionbank_id}`] || {};
        }
        
        for (const group of groupedUserAnswersValues) {
            group.total_point = group.questions.reduce((acc, { correct_point, is_correct }) => acc + (is_correct ? correct_point : 0), 0);
        }

        for (const group of groupedUserAnswersValues) {
            const questionbankReviews = group.questionbank_review;
            const questionbankReview = questionbankReviews.find(({ min_point, max_point }) => min_point <= group.total_point && group.total_point <= max_point);
            group.description_point = questionbankReview ? questionbankReview.description : null;
        }

        return {
            success: true,
            message: 'User answers have been loaded!',
            userAnswers: groupedUserAnswersValues
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to load user answers.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function filterUserAnswer(params) {
    try {
        const filterConditions = {};

        if (params?.questionbank_id) {
            filterConditions.questionbank_id = {
                [Op.in]: Array.isArray(params?.questionbank_id) ? params?.questionbank_id : [params?.questionbank_id]
            };
        }

        const userAnswers = await dbSAPPS.UserAnswer.findAll({
            where: filterConditions,
            order: [['updatedAt', 'DESC']],
            include: [
                {
                    model: dbSAPPS.User,
                    as: 'user',
                    attributes: ['id', 'full_name'],
                    include: [
                        {
                            model: dbSAPPS.UserChild,
                            as: 'userchilds'
                        }, {
                            model: dbSAPPS.UserAdditionalInfo,
                            as: 'useradditionalinfo'
                        }
                    ]
                },
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name', 'description'],
                    as: 'questionbank',
                    include: [
                        {
                            model: dbSAPPS.QuestionBankReview,
                            as: 'questionbankreviews',
                            attributes: ['id', 'min_point', 'max_point', 'description']
                        }
                    ]
                },
                {
                    model: dbSAPPS.Question,
                    as: 'question',
                    attributes: ['id', 'text'],
                    include: [{
                        model: dbSAPPS.Answer,
                        as: 'answers',
                        attributes: ['id', 'option', 'is_correct', 'correct_point', 'text']
                    }]
                }
            ],
            attributes: ['id', 'user_id', 'questionbank_id', 'type', 'option', 'is_correct', 'correct_point', 'question_id']
        });

        const questions = await dbSAPPS.Question.findAll({
            include: [
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name'],
                    as: 'questionbank'
                },
                {
                    model: dbSAPPS.Answer,
                    as: 'answers',
                    attributes: ['id', 'option', 'is_correct', 'correct_point']
                }
            ],
            attributes: ['id']
        });

        const questionsMap = questions.reduce((acc, question) => {
            acc[question.id] = {
                ...question,
                answers: question.answers.map(answer => ({
                    id: answer.id,
                    option: answer.option,
                    is_correct: answer.is_correct,
                    correct_point: answer.correct_point
                }))
            };
            return acc;
        }, {});

        const groupedUserAnswers = userAnswers.reduce((acc, userAnswer) => {
            const question = questionsMap[userAnswer.question_id];
            if (question && userAnswer.option === null) {
                userAnswer.option = question.answers.find(answer => answer.is_correct === userAnswer.is_correct)?.option || null;
            }

            const key = `${userAnswer.user_id}-${userAnswer.questionbank_id}-${userAnswer.type}`;
            if (!acc[key]) {
                acc[key] = {
                    user_id: userAnswer.user_id,
                    user: userAnswer.user,
                    questionbank_review: userAnswer.questionbank.questionbankreviews,
                    full_name: userAnswer.user.full_name,
                    questionbank_id: userAnswer.questionbank_id,
                    questionbank_display_name: userAnswer.questionbank.display_name,
                    questionbank_description: userAnswer.questionbank.description,
                    type: userAnswer.type,
                    questions: [],
                };
            }

            const questionExist = acc[key].questions.find(q => q.question.id === userAnswer.question_id);
            if (!questionExist) {
                acc[key].questions.push({
                    text: userAnswer.question.text,
                    question: userAnswer.question,
                    answer: userAnswer.option,
                    is_correct: userAnswer.is_correct,
                    correct_point: userAnswer.correct_point,
                    options: userAnswer.question.answers
                });
            }

            return acc;
        }, {});

        const groupedUserAnswersValues = Object.values(groupedUserAnswers);
        for (const group of groupedUserAnswersValues) {
            group.total_point = group.questions.reduce((acc, { correct_point, is_correct }) => acc + (is_correct ? correct_point : 0), 0);
        }

        for (const group of groupedUserAnswersValues) {
            const questionbankReviews = group.questionbank_review;
            const questionbankReview = questionbankReviews.find(({ min_point, max_point }) => min_point <= group.total_point && group.total_point <= max_point);
            group.description_point = questionbankReview ? questionbankReview.description : null;
        }

        return {
            success: true,
            message: 'User answers have been loaded!',
            userAnswers: groupedUserAnswersValues
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to load user answers.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

async function getUserAnswerByUserId(params) {
    try {

        console.log('params', params);
        if (!params.user_id || !params.questionbank_id || !params.type) {
            return {
                success: false,
                message: 'User ID, Question Bank ID and type are required!'
            };
        }

        const userAnswers = await dbSAPPS.UserAnswer.findAll({
            where: {
                user_id: parseInt(params.user_id),
                questionbank_id: parseInt(params.questionbank_id),
                type: params.type
            },
            order: [['updatedAt', 'DESC']],
            include: [
                {
                    model: dbSAPPS.User,
                    as: 'user',
                    attributes: ['id', 'full_name'],
                    include: [
                        {
                            model: dbSAPPS.UserChild,
                            as: 'userchilds'
                        }, {
                            model: dbSAPPS.UserAdditionalInfo,
                            as: 'useradditionalinfo'
                        }
                    ]
                },
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name', 'description'],
                    as: 'questionbank',
                    include: [
                        {
                            model: dbSAPPS.QuestionBankReview,
                            as: 'questionbankreviews',
                            attributes: ['id', 'min_point', 'max_point', 'description']
                        }
                    ]
                },
                {
                    model: dbSAPPS.Question,
                    as: 'question',
                    attributes: ['id', 'text'],
                    include: [{
                        model: dbSAPPS.Answer,
                        as: 'answers',
                        attributes: ['id', 'option', 'is_correct', 'correct_point', 'text']
                    }]
                }
            ],
            attributes: ['id', 'user_id', 'questionbank_id', 'type', 'option', 'is_correct', 'correct_point', 'question_id']
        });

        const questions = await dbSAPPS.Question.findAll({
            include: [
                {
                    model: dbSAPPS.QuestionBank,
                    attributes: ['id', 'display_name'],
                    as: 'questionbank'
                },
                {
                    model: dbSAPPS.Answer,
                    as: 'answers',
                    attributes: ['id', 'option', 'is_correct', 'correct_point']
                }
            ],
            attributes: ['id']
        });

        const questionsMap = questions.reduce((acc, question) => {
            acc[question.id] = {
                ...question,
                answers: question.answers.map(answer => ({
                    id: answer.id,
                    option: answer.option,
                    is_correct: answer.is_correct,
                    correct_point: answer.correct_point,
                    parameter: answer.parameter
                }))
            };
            return acc;
        }, {});

        const groupedUserAnswers = userAnswers.reduce((acc, userAnswer) => {
            const question = questionsMap[userAnswer.question_id];
            if (question && userAnswer.option === null) {
                userAnswer.option = question.answers.find(answer => answer.is_correct === userAnswer.is_correct)?.option || null;
            }

            const key = `${userAnswer.user_id}-${userAnswer.questionbank_id}-${userAnswer.type}`;
            if (!acc[key]) {
                acc[key] = {
                    user_id: userAnswer.user_id,
                    user: userAnswer.user,
                    questionbank_review: userAnswer.questionbank.questionbankreviews,
                    full_name: userAnswer.user.full_name,
                    questionbank_id: userAnswer.questionbank_id,
                    questionbank_display_name: userAnswer.questionbank.display_name,
                    questionbank_description: userAnswer.questionbank.description,
                    type: userAnswer.type,
                    questions: [],
                };
            }

            const questionExist = acc[key].questions.find(q => q.question.id === userAnswer.question_id);
            if (!questionExist) {
                acc[key].questions.push({
                    text: userAnswer.question.text,
                    question: userAnswer.question,
                    answer: userAnswer.option,
                    is_correct: userAnswer.is_correct,
                    correct_point: userAnswer.correct_point,
                    options: userAnswer.question.answers
                });
            }

            return acc;
        }, {});

        const groupedUserAnswersValues = Object.values(groupedUserAnswers);
        for (const group of groupedUserAnswersValues) {
            const countYa = group.questions.filter(q => q.answer === 'Ya').length;
            const countTidak = group.questions.filter(q => q.answer === 'Tidak').length;
            group.countYa = countYa;
            group.countTidak = countTidak;
        }

        for (const group of groupedUserAnswersValues) {
            group.total_point = group.questions.reduce((acc, { correct_point, is_correct }) => acc + (is_correct ? correct_point : 0), 0);
        }

        for (const group of groupedUserAnswersValues) {
            const questionbankReviews = group.questionbank_review;
            const questionbankReview = questionbankReviews.find(({ min_point, max_point }) => min_point <= group.total_point && group.total_point <= max_point);
            group.description_point = questionbankReview ? questionbankReview.description : null;
        }

        return {
            success: true,
            message: 'User answers have been loaded!',
            userAnswers: groupedUserAnswersValues
        };
    } catch (error) {
        const errorMessage = error.message || 'Failed to load user answers.';
        return {
            success: false,
            message: errorMessage
        };
    }
}

