const { Expenses, User, ItemCategory } = require("../../models");
const { NO_FILE, SERVER_ERROR } = 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, fn, col, literal } = require("sequelize");
const fastcsv = require('fast-csv');
const path = require('path');
const fs = require('fs');

const create = routeHandler(async (req, res, extras, next) => {
    const {
        user_id,
        item_category_id,
        expenses_amount,
        expenses_date,
        expenses_descp
    } = req.body;

    const { branch_id } = req.auth;

    const paramNames = ["User Id", "Item Category Id", "Expenses Amount", "Expenses Date"];
    const { hasNull, missingParams } = isNull([user_id, item_category_id, expenses_amount, expenses_date]);
    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 expenses = await Expenses.create({
            user_id,
            branch_id,
            item_category_id,
            expenses_amount,
            expenses_date,
            expenses_descp,
            created_by: req.auth.user_id,
        }, { transaction: extras.transaction });

        await extras.transaction.commit();
        const expensesDetails = await getDetails(expenses.expenses_id);
        return res.sendRes(expensesDetails, { message: 'Expenses created successfully', status: STATUS_CODE.OK });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const getAll = routeHandler(async (req, res, extras) => {
    const {search, from_date, to_date} = req.query;
    const { branch_id } = req.auth;
    let findObj = findObject(search, branch_id, from_date, to_date);
    try {
        const expenses = await Expenses.findAll({
            where: findObj,
            include: [
                {
                    model: User,
                    as: 'usersDetails',
                    attributes: ['user_id', 'fullname'],
                },
                {
                    model: ItemCategory,
                    as: 'itemCategories',
                    attributes: ['item_category_id', 'item_category_name'],
                },
                {
                    model: User,
                    as: 'user_created',
                    attributes: ['user_id', 'fullname'],
                },
                {
                    model: User,
                    as: 'user_updated',
                    attributes: ['user_id', 'fullname'],
                },
            ],
            order: [['created_at', 'DESC'],],
            ...req.paginate,
        });

        return res.sendRes(expenses, {
            message: 'Expenses loaded successfully',
            ...req.meta,
            total: await Expenses.count({
                where: findObj,
                include: [
                    {
                        model: ItemCategory,
                        as: 'itemCategories',
                        attributes: [],
                    },
                ],
            }),
            status: STATUS_CODE.OK,
        });

    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
}, false);

function findObject(search, branch_id, from_date, to_date) {
    let findObj = {};

    if (search) {
        findObj[Op.or] = [
            { '$itemCategories.item_category_name$': { [Op.substring]: search } }
        ];
    }

    if (from_date && to_date) {
        const startDate = new Date(from_date);
        const endDate = new Date(to_date);
        findObj.expenses_date = { [Op.between]: [startDate, endDate] };
    }

    if (branch_id && branch_id != 'undefined') findObj.branch_id = branch_id;

    return findObj;
}

const getById = routeHandler(async (req, res, extras) => {
    const { expenses_id } = req.params;

    try {
        const expenses = await Expenses.findOne({
            where: { expenses_id },
            include: [
                {
                    model: ItemCategory,
                    as: 'itemCategories',
                    attributes: ['item_category_id', 'item_category_name'],
                },
            ],
        });

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

        return res.sendRes(expenses, { message: 'Expenses 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 { expenses_id } = req.params;
    const {
        user_id,
        item_category_id,
        expenses_amount,
        expenses_date,
        expenses_descp
    } = req.body;

    try {
        const expenses = await Expenses.findOne({
            where: { expenses_id },
        });

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

        await expenses.update({
            user_id,
            item_category_id,
            expenses_amount,
            expenses_date,
            expenses_descp,
            updated_by: req.auth.user_id,
        }, { transaction: extras.transaction });

        await extras.transaction.commit();
        const expensesDetails = await getDetails(expenses.expenses_id);
        return res.sendRes(expensesDetails, { message: 'Expenses details 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 { expenses_id } = req.params;

    try {
        const expenses = await Expenses.findOne({ where: { expenses_id } });
        if (!expenses) {
            return sendAppError(extras, 'Expenses not found', STATUS_CODE.NOT_FOUND);
        }

        await expenses.update({
            deleted_by: req.auth.user_id,
        }, { transaction: extras.transaction });

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

async function getDetails(id) {
    return await Expenses.findOne({
        where: { expenses_id: id },
        include: [
            {
                model: User,
                as: 'usersDetails',
                attributes: ['user_id', 'fullname'],
            },
            {
                model: ItemCategory,
                as: 'itemCategories',
                attributes: ['item_category_id', 'item_category_name'],
            },
            {
                model: User,
                as: 'user_created',
                attributes: ['user_id', 'fullname'],
            },
            {
                model: User,
                as: 'user_updated',
                attributes: ['user_id', 'fullname'],
            },
        ],
    });
}

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