const { Op } = require("sequelize");
const { sendAppError } = require("../../utils/appError");
const { User, Credintial, UserRights } = require("../../models");
const routeHandler = require("../../utils/routeHandler");
const { STATUS_CODE, generateAccessToken } = require("../../utils/utility");
const { isNull } = require("../../utils/validation");
const { NO_PARAMS, SERVER_ERROR } = require("../../utils/errorMessage");
const bcrypt = require('bcrypt');

const login = routeHandler(async (req, res, extras) => {
    const {
        username,
        password,
        is_reminder
    } = req.body;

    const paramNames = ["User name", "Password"];
    const { hasNull, missingParams } = isNull([username, password]);
    if (hasNull) {
        const missingParamNames = missingParams.map(index => paramNames[index]);
        return sendAppError(extras, `${missingParamNames.join(', ')} ${missingParamNames.length > 1 ? 'are' : 'is'} required`, STATUS_CODE.BAD_REQUEST);
    }
    try {
        const findUser = await User.findOne({
            where: {
                [Op.or]: {
                    '$credintial.username$': username,
                },
            },
            include: [
                {
                    model: Credintial,
                    as: 'credintial',
                    attributes: ['username', 'password_hash'],
                },
            ],
            paranoid: false,
        });

        if (!findUser) {
            return sendAppError(extras, "User not found", STATUS_CODE.BAD_REQUEST);
        }

        if (findUser.deletedAt || findUser.status === false) {
            return sendAppError(extras, "User account deactivated", STATUS_CODE.BAD_REQUEST);
        }

        const passwordMatch = await bcrypt.compare(password, findUser.credintial.password_hash);
        if (!passwordMatch) {
            return sendAppError(extras, "Wrong Password", STATUS_CODE.BAD_REQUEST);
        }

        let userRights = null;
        if (findUser.role === 'Employee') {
            userRights = await UserRights.findOne({
                where: { user_id: findUser.user_id },
                attributes: ['user_rights_permission']
            });

            if (!userRights) {
                return sendAppError(extras, "User account permission blocked", STATUS_CODE.GONE);
            }
        }

        await extras.transaction.commit();

        const access_token = generateAccessToken({
            user_id: findUser.user_id,
        }, is_reminder);

        delete findUser.dataValues.credintial.dataValues.password_hash;

        return res.sendRes({ user: findUser, token: access_token, ...(userRights && { userRights: userRights.user_rights_permission }) }, {
            message: 'Login success',
            status: STATUS_CODE.OK,
        });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, SERVER_ERROR, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});


module.exports = {
    login,
};