const UserError = require('vn-loopback/util/user-error');
const LoopBackContext = require('loopback-context');

module.exports = Self => {
    require('../methods/sale/getClaimableFromTicket')(Self);
    require('../methods/sale/reserve')(Self);
    require('../methods/sale/deleteSales')(Self);
    require('../methods/sale/updatePrice')(Self);
    require('../methods/sale/updateQuantity')(Self);
    require('../methods/sale/updateConcept')(Self);
    require('../methods/sale/recalculatePrice')(Self);
    require('../methods/sale/canEdit')(Self);
    require('../methods/sale/usesMana')(Self);
    require('../methods/sale/clone')(Self);
    require('../methods/sale/getFromSectorCollection')(Self);

    Self.validatesPresenceOf('concept', {
        message: `Concept cannot be blank`
    });

    Self.observe('before save', async ctx => {
        const models = Self.app.models;
        const changes = ctx.data || ctx.instance;
        const instance = ctx.currentInstance;

        const newQuantity = changes?.quantity;
        if (newQuantity == null) return;

        const loopBackContext = LoopBackContext.getCurrentContext();
        ctx.req = loopBackContext.active;

        const ticketId = changes?.ticketFk || instance?.ticketFk;
        const itemId = changes?.itemFk || instance?.itemFk;
        const oldQuantity = instance?.quantity ?? null;
        const quantityAdded = newQuantity - oldQuantity;
        const isReduction = oldQuantity && newQuantity <= oldQuantity;

        const ticket = await models.Ticket.findById(
            ticketId,
            {
                fields: ['id', 'clientFk', 'warehouseFk', 'addressFk', 'agencyModeFk', 'shipped', 'landed'],
                include: {
                    relation: 'client',
                    scope: {
                        fields: ['id', 'typeFk'],
                        include: {
                            relation: 'type',
                            scope: {
                                fields: ['code', 'description']
                            }
                        }
                    }
                }
            },
            ctx.options);
        if (ticket?.client()?.typeFk === 'loses') return;

        const isRefund = await models.TicketRefund.findOne({
            fields: ['id'],
            where: {refundTicketFk: ticketId}
        }, ctx.options);
        if (isRefund) return;

        if (newQuantity < 0)
            throw new UserError('You can only add negative amounts in refund tickets');

        const item = await models.Item.findOne({
            fields: ['family'],
            where: {id: itemId},
        }, ctx.options);
        if (item.family == 'EMB') return;

        if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*')) return;

        await models.Sale.rawSql(`CALL catalog_calcFromItem(?,?,?,?)`, [
            ticket.landed,
            ticket.addressFk,
            ticket.agencyModeFk,
            itemId
        ],
        ctx.options);

        const [itemInfo] = await models.Sale.rawSql(`SELECT available FROM tmp.ticketCalculateItem`, null, ctx.options);
        const available = itemInfo?.available;

        if ((!isReduction && !available) || available < quantityAdded)
            throw new UserError(`This item is not available`);

        if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;

        const itemMinimalQuantity = await models.ItemMinimumQuantity.find({
            fields: ['quantity', 'warehouseFk'],
            where: {
                itemFk: itemId,
                started: {lte: ticket.shipped},
                or: [
                    {ended: {gte: ticket.shipped}},
                    {ended: null}
                ],
                // eslint-disable-next-line no-dupe-keys
                or: [
                    {warehouseFk: ticket.warehouseFk},
                    {warehouseFk: null}
                ]
            },
        }, ctx.options);

        const minQuantity = itemMinimalQuantity.reduce((selected, item) => {
            if (item.warehouseFk === ticket.warehouseFk) return item;
            if (item.warehouseFk === null && !selected) return item;
            return selected;
        }, null);

        if (newQuantity < minQuantity?.quantity && newQuantity != available)
            throw new UserError('The amount cannot be less than the minimum');

        if (ctx.isNewInstance || isReduction) return;

        const [saleGrouping] = await models.Sale.rawSql(`
            SELECT t.price newPrice
                FROM tmp.ticketComponentPrice t
                ORDER BY (t.grouping <= ?) DESC, t.grouping ASC
                LIMIT 1`,
        [quantityAdded],
        ctx.options);

        await models.Sale.rawSql(`
            DROP TEMPORARY TABLE IF EXISTS
                tmp.ticketCalculateItem,
                tmp.ticketComponentPrice,
                tmp.ticketComponent,
                tmp.ticketLot,
                tmp.zoneGetShipped;
            `, null, ctx.options);

        if (!saleGrouping?.newPrice || saleGrouping.newPrice > instance.price)
            throw new UserError('The price of the item changed');
    });
};