const { Settings, Asset } = require("../../models");
const { NO_PARAMS } = require("../../utils/errorMessage");
const { STATUS_CODE } = require("../../utils/utility");
const routeHandler = require("../../utils/routeHandler");
const { isNull } = require("../../utils/validation");
const { sendAppError } = require("../../utils/appError");
const { Op } = require("sequelize");

const create = routeHandler(async (req, res, extras) => {
    const {
        setting_name,
        setting_type,
        setting_desc
    } = req.body;

    const { user_id } = req.auth;

    const types = ["PRIVACY_AND_POLICY", "TERMS_AND_CONDITIONS", "GENDRAL_INFROMATION", "ABOUT_US"];

    const paramNames = ["Settings Name", "Settings type", "Settings Description"];
    const { hasNull, missingParams } = isNull([setting_name, setting_type, setting_desc]);

    if (hasNull || !types.includes(setting_type)) {
        const missingParamNames = missingParams.map(index => paramNames[index]);
        if (!types.includes(setting_type)) {
            return sendAppError(extras, `Invalid type '${setting_type}'. Expected types are: ${types.join(', ')}`, STATUS_CODE.BAD_REQUEST);
        }
        return sendAppError(extras, `${missingParamNames.join(', ')} ${missingParamNames.length > 1 ? 'are' : 'is'} required`, STATUS_CODE.BAD_REQUEST);
    }

    try {
        const findSettings = await Settings.findOne({ where: { setting_name } });

        if (findSettings) {
            return sendAppError(extras, 'Settings pages already exist', STATUS_CODE.BAD_REQUEST);
        }

        const settings = await Settings.create({
            setting_name: setting_name.replace(/ /g, "_").toUpperCase(),
            setting_type: setting_type.replace(/ /g, "_").toUpperCase(),
            setting_desc,
            created_by: user_id,
        }, { transaction: extras.transaction });

        await extras.transaction.commit();
        return res.sendRes(settings, {
            message: 'Settings pages saved successfully',
            status: STATUS_CODE.CREATED,
        });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const getAll = routeHandler(async (req, res, extras) => {
    const setting_type = req.query?.setting_type;
    const setting_name = req.query?.setting_name;

    let findObj = findObject(setting_type, setting_name);

    try {
        const settings = await Settings.findAll({
            where: {
                [Op.and]: [
                    ...findObj,
                    { setting_type: { [Op.ne]: 'CONTACT_US' } }
                ],
            },
            order: [['created_at', 'DESC']],
            ...req.paginate,
        });
        return res.sendRes(settings, {
            message: "Settings pages loaded successfully",
            ...req.meta,
            total: await Settings.count({
                where: {
                    [Op.and]: [
                        ...findObj,
                        { setting_type: { [Op.ne]: 'CONTACT_US' } }
                    ],
                },
            }),
            status: STATUS_CODE.OK,
        });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
}, false);

function findObject(setting_type, setting_name) {
    const findObj = [];
    if (setting_type) {
        findObj.push({ setting_type });
    }
    if (setting_name) {
        findObj.push({ setting_name });
    }
    return findObj;
}

const getById = routeHandler(async (req, res, extras) => {
    const { setting_id } = req.params;
    try {
        const settings = await Settings.findOne({ where: { setting_id } });

        if (!settings) {
            return sendAppError(extras, 'Settings pages not found', STATUS_CODE.NOT_FOUND);
        }

        return res.sendRes(settings, { message: 'Settings pages loaded successfully', status: STATUS_CODE.OK });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
}, false);

const updateById = routeHandler(async (req, res, extras) => {
    const { setting_id } = req.params;
    const { user_id } = req.auth;
    const {
        setting_name,
        setting_type,
        setting_desc
    } = req.body;

    const types = ["PRIVACY_AND_POLICY", "TERMS_AND_CONDITIONS", "GENDRAL_INFROMATION", "ABOUT_US"];

    const paramNames = ["Settings Name", "Settings type", "Settings Description"];
    const { hasNull, missingParams } = isNull([setting_name, setting_type, setting_desc]);

    if (hasNull || !types.includes(setting_type)) {
        const missingParamNames = missingParams.map(index => paramNames[index]);
        if (!types.includes(setting_type)) {
            return sendAppError(extras, `Invalid type '${setting_type}'. Expected types are: ${types.join(', ')}`, STATUS_CODE.BAD_REQUEST);
        }
        return sendAppError(extras, `${missingParamNames.join(', ')} ${missingParamNames.length > 1 ? 'are' : 'is'} required`, STATUS_CODE.BAD_REQUEST);
    }
    try {
        const settings = await Settings.findOne({ where: { setting_id } });
        if (!settings) {
            return sendAppError(extras, 'Settings not found', STATUS_CODE.NOT_FOUND);
        }

        const findSettings = await Settings.findOne({
            where: {
                setting_name,
                setting_id: {
                    [Op.not]: setting_id,
                }
            }
        });
        if (findSettings) {
            return sendAppError(extras, "Settings name already exist", STATUS_CODE.BAD_REQUEST);
        }

        await settings.update({
            setting_name: setting_name.replace(/ /g, "_").toUpperCase(),
            setting_type: setting_type.replace(/ /g, "_").toUpperCase(),
            setting_desc,
            updated_by: user_id,
        }, { transaction: extras.transaction });

        await extras.transaction.commit();
        return res.sendRes(settings, { message: 'Settings updated successfully', status: STATUS_CODE.OK, });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const deleteById = routeHandler(async (req, res, extras) => {
    const { setting_id } = req.params;
    const { user_id } = req.auth;
    try {
        const settings = await Settings.findOne({ where: { setting_id } });

        if (!settings) {
            return sendAppError(extras, 'Settings not found', STATUS_CODE.NOT_FOUND);
        }

        await settings.update({
            deleted_by: user_id,
        }, { transaction: extras.transaction });

        await settings.destroy({ transaction: extras.transaction });

        await extras.transaction.commit();
        return res.sendRes(null, { message: 'Settings deleted successfully', status: STATUS_CODE.OK });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const createContactUs = routeHandler(async (req, res, extras) => {
    const {
        company_name,
        company_email,
        company_number,
        company_address,
        company_gst_no,
    } = req.body;
    const { file } = req;

    const { user_id } = req.auth;

    const paramNames = ["Company Name", "Company Email", "Company Office no", "Company Address"];
    const { hasNull, missingParams } = isNull([company_name, company_email, company_number, company_address]);
    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 findSettings = await Settings.findOne({ where: { setting_type: 'CONTACT_US' } });
        if (findSettings) {
            return sendAppError(extras, 'Contact Us pages already exist', STATUS_CODE.BAD_REQUEST);
        }

        const settings = await Settings.create({
            setting_name: 'CONTACT_US',
            setting_type: 'CONTACT_US',
            setting_desc: JSON.stringify({
                company_name: company_name,
                company_email: company_email,
                company_number: company_number,
                company_address: company_address,
                company_gst_no: company_gst_no
            }),
            created_by: user_id,
        }, { transaction: extras.transaction });

        const companyLogo = await Asset.findOne({ where: { type: 'COMPANY_LOGO' } });
        if (file && companyLogo) {
            await companyLogo.update({
                name: file.originalname,
                data: file.buffer,
                mimetype: file.mimetype,
                updated_by: user_id,
            }, { transaction: extras.transaction, })
        }
        else if (file) {
            await Asset.create({
                type: 'COMPANY_LOGO',
                owner_id: user_id,
                name: file.originalname,
                data: file.buffer,
                mimetype: file.mimetype,
                created_by: user_id,
            }, { transaction: extras.transaction, })
        }

        await extras.transaction.commit();
        return res.sendRes(settings, {
            message: 'Contact details successfully',
            status: STATUS_CODE.CREATED,
        });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const getContactUs = routeHandler(async (req, res, extras) => {
    const { branch_id } = req.auth;
    try {
        const settings = await Settings.findAll({
            where: {
                branch_id
            },
            order: [['created_at', 'DESC'],],
            ...req.paginate,
        });
        if (settings && settings.length > 0) {
            settings[0].setting_desc = JSON.parse(settings[0].setting_desc)
            settings[0].setting_desc.logo = await Asset.findOne({ where: { type: 'COMPANY_LOGO' } });
        }

        return res.sendRes(settings, {
            message: "Contact us loaded successfully",
            ...req.meta,
            total: await Settings.count(),
            status: STATUS_CODE.OK,
        });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
}, false);

const updateContactUs = routeHandler(async (req, res, extras) => {
    const setting_id = req.params.contact_us_id;
    const { user_id } = req.auth;

    const {
        company_name,
        company_email,
        company_number,
        company_address,
        company_gst_no
    } = req.body;

    const { file } = req;
    try {
        const settings = await Settings.findOne({ where: { setting_id } });
        if (!settings) {
            return sendAppError(extras, 'Settings not found', STATUS_CODE.NOT_FOUND);
        }

        await settings.update({
            setting_desc: JSON.stringify({
                company_name: company_name,
                company_email: company_email,
                company_number: company_number,
                company_address: company_address,
                company_gst_no: company_gst_no
            }),
        }, { transaction: extras.transaction });

        const companyLogo = await Asset.findOne({ where: { type: 'COMPANY_LOGO' } });
        if (file && companyLogo) {
            await companyLogo.update({
                name: file.originalname,
                data: file.buffer,
                mimetype: file.mimetype,
                updated_by: user_id,
            }, { transaction: extras.transaction, })
        }
        else if (file) {
            await Asset.create({
                type: 'COMPANY_LOGO',
                owner_id: user_id,
                name: file.originalname,
                data: file.buffer,
                mimetype: file.mimetype,
                created_by: user_id,
            }, { transaction: extras.transaction, })
        }

        if (settings) {
            settings.setting_desc = JSON.parse(settings.setting_desc)
            settings.setting_desc.logo = await Asset.findOne({ where: { type: 'COMPANY_LOGO' } });
        }
        await extras.transaction.commit();
        return res.sendRes(settings, { message: 'Settings updated successfully', status: STATUS_CODE.OK });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

module.exports = {
    create,
    getAll,
    getById,
    updateById,
    deleteById,
    createContactUs,
    getContactUs,
    updateContactUs
};
