const db = require('../core/database');
const Email = require('../core/email');
const smtp = require('../core/smtp');
const config = require('../core/config');

module.exports = app => {
    app.get('/api/closure/all', async function(req, res, next) {
        try {
            const reqArgs = req.args;
            if (!reqArgs.to)
                throw new Error('The argument to is required');

            res.status(200).json({
                message: 'Task executed successfully'
            });

            const tickets = await db.rawSql(`
                SELECT 
                    t.id
                FROM expedition e
                    JOIN ticket t ON t.id = e.ticketFk
                    JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission
                    JOIN ticketState ts ON ts.ticketFk = t.id
                    JOIN alertLevel al ON al.alertLevel = ts.alertLevel
                WHERE al.code = 'PACKED'
                    AND DATE(t.shipped) BETWEEN DATE_ADD(:to, INTERVAL -2 DAY) 
                        AND util.dayEnd(:to)
                    AND t.refFk IS NULL
                GROUP BY e.ticketFk`, {
                to: reqArgs.to
            });
            const ticketIds = tickets.map(ticket => ticket.id);

            await closeAll(ticketIds, req.args);
            await db.rawSql(`
                UPDATE ticket t
                    JOIN ticketState ts ON t.id = ts.ticketFk
                    JOIN alertLevel al ON al.alertLevel = ts.alertLevel
                    JOIN agencyMode am ON am.id = t.agencyModeFk
                    JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
                    JOIN zone z ON z.id = t.zoneFk
                SET t.routeFk = NULL
                    WHERE DATE(t.shipped) BETWEEN DATE_ADD(:to, INTERVAL -2 DAY) 
                        AND util.dayEnd(:to)
                        AND al.code NOT IN('DELIVERED','PACKED')
                        AND t.routeFk
                        AND z.name LIKE '%MADRID%'`, {
                to: reqArgs.to
            });
        } catch (error) {
            next(error);
        }
    });

    app.get('/api/closure/by-ticket', async function(req, res, next) {
        try {
            const reqArgs = req.args;
            if (!reqArgs.ticketId)
                throw new Error('The argument ticketId is required');

            res.status(200).json({
                message: 'Task executed successfully'
            });

            const tickets = await db.rawSql(`
                SELECT 
                    t.id
                FROM expedition e
                    JOIN ticket t ON t.id = e.ticketFk
                    JOIN ticketState ts ON ts.ticketFk = t.id
                    JOIN alertLevel al ON al.alertLevel = ts.alertLevel
                WHERE al.code = 'PACKED'
                    AND t.id = :ticketId
                    AND t.refFk IS NULL
                GROUP BY e.ticketFk`, {
                ticketId: reqArgs.ticketId
            });
            const ticketIds = tickets.map(ticket => ticket.id);

            await closeAll(ticketIds, reqArgs);
        } catch (error) {
            next(error);
        }
    });

    app.get('/api/closure/by-agency', async function(req, res, next) {
        try {
            const reqArgs = req.args;
            if (!reqArgs.agencyModeId)
                throw new Error('The argument agencyModeId is required');

            if (!reqArgs.warehouseId)
                throw new Error('The argument warehouseId is required');

            if (!reqArgs.to)
                throw new Error('The argument to is required');

            res.status(200).json({
                message: 'Task executed successfully'
            });

            const agenciesId = reqArgs.agencyModeId.split(',');
            const tickets = await db.rawSql(`
                SELECT 
                    t.id
                FROM expedition e
                    JOIN ticket t ON t.id = e.ticketFk
                    JOIN ticketState ts ON ts.ticketFk = t.id
                    JOIN alertLevel al ON al.alertLevel = ts.alertLevel
                WHERE al.code = 'PACKED'
                    AND t.agencyModeFk IN(:agencyModeId) 
                    AND t.warehouseFk = :warehouseId
                    AND DATE(t.shipped) BETWEEN DATE_ADD(:to, INTERVAL -2 DAY) 
                    AND util.dayEnd(:to)
                    AND t.refFk IS NULL
                GROUP BY e.ticketFk`, {
                agencyModeId: agenciesId,
                warehouseId: reqArgs.warehouseId,
                to: reqArgs.to
            });
            const ticketIds = tickets.map(ticket => ticket.id);

            await closeAll(ticketIds, reqArgs);
        } catch (error) {
            next(error);
        }
    });

    app.get('/api/closure/by-route', async function(req, res, next) {
        try {
            const reqArgs = req.args;
            if (!reqArgs.routeId)
                throw new Error('The argument routeId is required');

            res.status(200).json({
                message: 'Task executed successfully'
            });

            const tickets = await db.rawSql(`
                SELECT 
                    t.id
                FROM expedition e
                    JOIN ticket t ON t.id = e.ticketFk
                    JOIN ticketState ts ON ts.ticketFk = t.id
                    JOIN alertLevel al ON al.alertLevel = ts.alertLevel
                WHERE al.code = 'PACKED'
                    AND t.routeFk = :routeId
                    AND t.refFk IS NULL
                GROUP BY e.ticketFk`, {
                routeId: reqArgs.routeId
            });
            const ticketIds = tickets.map(ticket => ticket.id);

            await closeAll(ticketIds, reqArgs);
        } catch (error) {
            next(error);
        }
    });

    async function closeAll(ticketIds, reqArgs) {
        const failedtickets = [];
        const tickets = await db.rawSql(`
            SELECT 
                t.id,
                t.clientFk,
                c.name clientName,
                c.email recipient,
                c.salesPersonFk,
                c.isToBeMailed,
                c.hasToInvoice,
                co.hasDailyInvoice,
                eu.email salesPersonEmail
            FROM ticket t
                JOIN client c ON c.id = t.clientFk
                JOIN province p ON p.id = c.provinceFk
                JOIN country co ON co.id = p.countryFk
                LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
            WHERE t.id IN(?)`, [ticketIds]);

        for (const ticket of tickets) {
            try {
                await db.rawSql(`CALL vn.ticket_close(:ticketId)`, {
                    ticketId: ticket.id
                });

                const hasToInvoice = ticket.hasToInvoice && ticket.hasDailyInvoice;
                if (!ticket.salesPersonFk || !ticket.isToBeMailed || hasToInvoice) continue;

                if (!ticket.recipient) {
                    const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong> 
                        al cliente <strong>${ticket.clientFk}</strong> porque no tiene un email especificado.<br/><br/>
                        Para dejar de recibir esta notificación, asígnale un email o desactiva la notificación por email para este cliente.`;
                    smtp.send({
                        to: ticket.salesPersonEmail,
                        subject: 'No se ha podido enviar el albarán',
                        html: body
                    });

                    continue;
                }

                const args = Object.assign({
                    ticketId: ticket.id,
                    recipientId: ticket.clientFk,
                    recipient: ticket.recipient,
                    replyTo: ticket.salesPersonEmail
                }, reqArgs);

                const email = new Email('delivery-note-link', args);
                await email.send();
            } catch (error) {
                // Domain not found
                if (error.responseCode == 450)
                    return invalidEmail(ticket);

                // Save tickets on a list of failed ids
                failedtickets.push({
                    id: ticket.id,
                    stacktrace: error
                });
            }
        }

        // Send email with failed tickets
        if (failedtickets.length > 0) {
            let body = 'This following tickets have failed:<br/><br/>';

            for (ticket of failedtickets) {
                body += `Ticket: <strong>${ticket.id}</strong>
                    <br/> <strong>${ticket.stacktrace}</strong><br/><br/>`;
            }

            smtp.send({
                to: config.app.reportEmail,
                subject: '[API] Nightly ticket closure report',
                html: body
            });
        }
    }

    async function invalidEmail(ticket) {
        await db.rawSql(`UPDATE client SET email = NULL WHERE id = :clientId`, {
            clientId: ticket.clientFk
        });

        const oldInstance = `{"email": "${ticket.recipient}"}`;
        const newInstance = `{"email": ""}`;
        await db.rawSql(`
            INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) 
                VALUES (:clientId, :userId, 'UPDATE', 'Client', :oldInstance, :newInstance)`, {
            clientId: ticket.clientFk,
            userId: null,
            oldInstance: oldInstance,
            newInstance: newInstance
        });

        const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong> 
        al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong> 
        porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta o no está disponible.<br/><br/>
        Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. 
        Actualiza la dirección de email con una correcta.`;

        smtp.send({
            to: ticket.salesPersonEmail,
            subject: 'No se ha podido enviar el albarán',
            html: body
        });
    }
};