const { Order, OrderDetails, Product, Customer, Area, Landmark } = require("../../models");
const { NO_PARAMS, 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 } = require("sequelize");
const moment = require("moment");

const create = routeHandler(async (req, res, extras, next) => {
    const {
        collection_day,
        order_date,
        order_no,
        customer_id,
        customer_name,
        mobile_number,
        area_id,
        landmark_id,
        sub_amount,
        advance_amount,
        total_amount,
        orderDetails
    } = req.body;

    if (!collection_day) {
        return sendAppError(extras, "Day is required", STATUS_CODE.BAD_REQUEST);
    }
    
    const dayFormat = collection_day.replace(/ /g, "_").toUpperCase();
    const types = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"];
    const { user_id, branch_id } = req.auth;

    const paramNames = ["Collection Day", "Order Date", "Customer Name", "Mobile Number", "Area Name", "Landmark Name", "Sub Amount", "Total Amount"];
    const { hasNull, missingParams } = isNull([collection_day, order_date, customer_name, mobile_number, area_id, landmark_id, sub_amount, total_amount]);

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

    if (!Array.isArray(orderDetails) || orderDetails.length === 0) {
        return sendAppError(extras, NO_PARAMS, STATUS_CODE.BAD_REQUEST);
    }

    try {
        let customer;
        const findCustomer = await Customer.findOne({
            where: {
                mobile_number
            },
        });

        if (!findCustomer) {
            customer = await Customer.create({
                branch_id,
                area_id,
                landmark_id,
                customer_name,
                mobile_number,
                created_by: user_id
            }, { transaction: extras.transaction });
        }
        const order = await Order.create({
            branch_id,
            collection_day: dayFormat,
            order_date: new Date(order_date),
            order_no,
            customer_id: findCustomer ? findCustomer.customer_id : customer.customer_id,
            area_id,
            landmark_id,
            sub_amount,
            advance_amount,
            total_amount,
            created_by: user_id
        }, { transaction: extras.transaction });

        const tableRowsData = orderDetails.map(row => ({
            order_id: order.order_id,
            product_id: row.product_id,
            price: row.price,
            quantity: row.quantity,
            amount: row.amount,
            created_by: user_id
        }));

        await OrderDetails.bulkCreate(tableRowsData, { transaction: extras.transaction });
        await extras.transaction.commit();

        const newOrderDetails = await Order.findOne({
            where: { order_id: order.order_id },
            include: [{ model: OrderDetails, as: 'orderDetails' }]
        });
        return res.sendRes(newOrderDetails, { message: 'Order created successfully', status: STATUS_CODE.OK });
    } catch (error) {
        await extras.transaction.rollback();
        return sendAppError(extras, SERVER_ERROR, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
});

const getAll = routeHandler(async (req, res, extras) => {
    const search = req.query?.search;
    const { branch_id } = req.auth;
    let findObj = findObject(search, branch_id);
    try {
        const orders = await Order.findAll({
            where: findObj,
            include: [
                {
                    model: Customer,
                    as: 'customer',
                    attributes: ['customer_id', 'customer_name', 'mobile_number'],
                },
                {
                    model: Area,
                    as: 'area',
                    attributes: ['area_id', 'area_name'],
                },
                {
                    model: Landmark,
                    as: 'landmark',
                    attributes: ['landmark_id', 'landmark_name'],
                },
                {
                    model: OrderDetails,
                    as: 'orderDetails',
                    include: [
                        {
                            model: Product,
                            as: 'product',
                            attributes: ['product_id', 'product_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(orders, {
            message: 'Orders loaded successfully',
            ...req.meta,
            total: await Order.count({
                where: findObj,
                include: [
                    {
                        model: Customer,
                        as: 'customer',
                        attributes: [],
                    },
                    {
                        model: Area,
                        as: 'area',
                        attributes: [],
                    },
                    {
                        model: Landmark,
                        as: 'landmark',
                        attributes: [],
                    },
                    {
                        model: OrderDetails,
                        as: 'orderDetails',
                        include: [
                            {
                                model: Product,
                                as: 'product',
                                attributes: [],
                            }
                        ]
                    },
                    {
                        model: User,
                        as: 'user_created',
                        attributes: [],
                    },
                    {
                        model: User,
                        as: 'user_updated',
                        attributes: [],
                    },
                ],
            }),
            status: STATUS_CODE.OK,
        });

    } catch (error) {
        console.error('Error fetching orders:', error);
        await extras.transaction.rollback();
        return sendAppError(extras, error.message, STATUS_CODE.INTERNAL_SERVER_ERROR);
    }
}, false);

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

    if (search) {
        findObj[Op.or] = [
            { collection_day: { [Op.substring]: search } },
            { order_no: { [Op.substring]: search } },
            { '$customer.customer_name$': { [Op.substring]: search } },
            { '$customer.mobile_number$': { [Op.substring]: search } },
            { '$area.area_name$': { [Op.substring]: search } },
            { '$landmark.landmark_name$': { [Op.substring]: search } }
        ];
    }

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

    return findObj;
}

const getOrderId = routeHandler(async (req, res, extras) => {
    try {
        const orders = await Order.count({ paranoid: false, });

        return res.sendRes(orders, {
            message: 'Orders loaded successfully',
            ...req.meta,
            status: STATUS_CODE.OK,
        });

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

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

    try {
        const order = await Order.findOne({
            where: { order_id },
            include: [
                {
                    model: Customer,
                    as: 'customer',
                    attributes: ['customer_id', 'customer_name', 'mobile_number'],
                },
                {
                    model: Area,
                    as: 'area',
                    attributes: ['area_id', 'area_name'],
                },
                {
                    model: Landmark,
                    as: 'landmark',
                    attributes: ['landmark_id', 'landmark_name'],
                },
                {
                    model: OrderDetails,
                    as: 'orderDetails',
                    include: [
                        {
                            model: Product,
                            as: 'product',
                            attributes: ['product_id', 'product_name'],
                        }
                    ]
                },
            ],
        });

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

        return res.sendRes(order, { message: 'Order 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 { order_id } = req.params;
    const { user_id } = req.auth;
    const {
        collection_day,
        order_date,
        order_no,
        customer_id,
        sub_amount,
        advance_amount,
        total_amount,
        orderDetails
    } = req.body;

    const dayFormat = collection_day.replace(/ /g, "_").toUpperCase();
    const types = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"];

    if (!types.includes(dayFormat)) {
        return sendAppError(extras, `Invalid type '${collection_day}'. Expected types are: ${types.join(', ')}`, STATUS_CODE.BAD_REQUEST);
    }

    try {
        const order = await Order.findOne({ where: { order_id } });

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

        if (!Array.isArray(orderDetails) || orderDetails.length === 0) {
            return sendAppError(extras, NO_PARAMS, STATUS_CODE.BAD_REQUEST);
        }

        await OrderDetails.destroy({ where: { order_id }, transaction: extras.transaction });

        await order.update({
            collection_day: dayFormat,
            order_date: new Date(order_date),
            order_no,
            customer_id,
            sub_amount,
            advance_amount,
            total_amount,
            updated_by: user_id,
        }, { transaction: extras.transaction });

        const tableRowsData = orderDetails.map(row => ({
            order_id,
            product_id: row.product_id,
            quantity: row.quantity,
            price: row.price,
            amount: row.amount,
            created_by: user_id,
        }));

        await OrderDetails.bulkCreate(tableRowsData, { transaction: extras.transaction });
        await extras.transaction.commit();

        const updatedOrderDetails = await Order.findOne({
            where: { order_id },
            include: [{ model: OrderDetails, as: 'orderDetails' }]
        });

        return res.sendRes(updatedOrderDetails, { message: 'Order 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 { order_id } = req.params;

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

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

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

        const orderDetails = await OrderDetails.findAll({ where: { order_id }, transaction: extras.transaction });

        if (orderDetails.length > 0) {
            await OrderDetails.destroy({ where: { order_id }, transaction: extras.transaction });
        }

        await extras.transaction.commit();
        return res.sendRes(null, { message: 'Order details deleted 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,
    getOrderId,
};
