// Express routing
const express = require('express');
const router = express.Router();
// Database connection
const db = require('../db');
// Param validation and transformation functions
const paramValidation = require('../param_validation');
const paramTransformation = require('../param_transformation');
// Error handling functions
const errorHandling = require('../error_handling');
const { DatabaseError } = require('pg');
const { sendEmail } = require('../services/emailService');

/* GET Notifications */
router.get('/notifications', async function (req, res, next) {
    // Validate query params
    const team_id = req.query.team_id;
    if (team_id) {
        let validation_tuple = await paramValidation.validate_team_id(
            team_id,
            paramValidation.ParamType.QUERY
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(404)
                .json(
                    errorHandling.return_error_404(validation_tuple.error_msg)
                );
        }
    }

    // Assemble SQL query based on provided query params
    const query = db.prepare_select_statement('notifications', req.query);
    // Retrieve rows from database
    try {
        const result = await db.query(query, Object.values(req.query));

        return res.status(200).json(result.rows);
    } catch (error) {
        return res
            .status(500)
            .json(errorHandling.return_error_500(error.message));
    }
});

/* CREATE Notification */
router.post('/notifications', async function (req, res, next) {
    // Create dict to store SQL "INSERT INTO"/"VALUES" params
    let params = {};
    // Retrieve request body properties
    const request_id = req.body.request_id || null;
    const team_id = req.body.team_id || null;
    const generated_message = req.body.generated_message || null;
    const free_text = req.body.free_text || null;
    const for_instructor =
        req.body.for_instructor === undefined ||
        req.body.for_instructor === null
            ? false
            : req.body.for_instructor;

    let validation_tuple;
    if (request_id) {
        validation_tuple = await paramValidation.validate_request_id(
            request_id,
            paramValidation.ParamType.BODY
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(404)
                .json(
                    errorHandling.return_error_404(validation_tuple.error_msg)
                );
        }

        params.request_id = request_id;
    }

    validation_tuple = await paramValidation.validate_team_id(
        team_id,
        paramValidation.ParamType.BODY
    );
    if (!validation_tuple.is_valid) {
        return res
            .status(404)
            .json(errorHandling.return_error_404(validation_tuple.error_msg));
    }
    params.team_id = team_id;

    if (generated_message) {
        validation_tuple = paramValidation.build_validation_tuple(
            generated_message,
            paramValidation.checkString(generated_message),
            'Failed to convert value for generated_message to a valid string.'
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(400)
                .json(
                    errorHandling.return_error_400(validation_tuple.error_msg)
                );
        }

        params.generated_message = generated_message;
    }
    if (free_text) {
        validation_tuple = paramValidation.build_validation_tuple(
            free_text,
            paramValidation.checkString(free_text),
            'Failed to convert value for free_text to a valid string.'
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(400)
                .json(
                    errorHandling.return_error_400(validation_tuple.error_msg)
                );
        }

        params.free_text = free_text;
    }
    if (for_instructor !== null) {
        validation_tuple = paramValidation.build_validation_tuple(
            for_instructor,
            paramValidation.checkBoolean(for_instructor),
            'Failed to covert value ' +
                for_instructor +
                ' for for_instructor to a boolean.'
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(400)
                .json(
                    errorHandling.return_error_400(validation_tuple.error_msg)
                );
        }
    }
    params.for_instructor = for_instructor;

    // Generate notification timestamp
    params.notification_timestamp = new Date().toISOString();

    // Assemble SQL query based on provided query params
    const query = db.prepare_insert_statement('notifications', params);
    // Attempt to write row to database
    try {
        const result = await db.query(query, Object.values(params));
        // Make sure row was created
        if (!result.rowCount) {
            throw new DatabaseError(
                'Database insertion failed: no rows affected.'
            );
        }

        try {
            let emailParams = {
                teams: [
                    {
                        team_id: team_id,
                    },
                ],
            };

            const from =
                'users JOIN students ON users.user_id = students.user_id JOIN teams ON students.team_id = teams.team_id';

            const emailQuery = db.prepare_complex_select_statement(
                from,
                emailParams
            );
            const emailResult = await db.query(emailQuery, [team_id]);

            const isCustom = generated_message
                .toLowerCase()
                .includes('message from');

            const isDelivery = free_text.toLowerCase().includes('delivered');

            for (const user of emailResult.rows) {
                const shouldSend =
                    user.receive_emails === 'always' ||
                    (user.receive_emails === 'on_deliveries' && isDelivery) ||
                    isCustom;

                if (shouldSend) {
                    sendEmail({
                        to: user.email_address,
                        subject: 'New ShipME Notification',
                        html: `
                        <div style="font-size: 1.5rem; color: black; font-weight: bold; margin-bottom: 0.5rem;">
                            ${generated_message}
                        </div>
                        ${free_text ? `<div style="font-size: 1rem; color: black;">${free_text}</div>` : ''}
                        `,
                    });
                }
            }
        } catch (error) {
            return res
                .status(500)
                .json(errorHandling.return_error_500(error.message));
        }

        return res.status(201).json(result.rows[0]);
    } catch (error) {
        return res
            .status(500)
            .json(errorHandling.return_error_500(error.message));
    }
});

/* GET Notification by ID */
router.get('/notifications/:notification_id', async function (req, res, next) {
    const notification_id = req.params.notification_id;
    if (notification_id) {
        let validation_tuple = await paramValidation.validate_notification_id(
            notification_id,
            paramValidation.ParamType.PATH
        );
        if (!validation_tuple.is_valid) {
            return res
                .status(404)
                .json(
                    errorHandling.return_error_404(validation_tuple.error_msg)
                );
        }
    }
    // Retrieve row from database
    try {
        const result = await db.query(
            'SELECT * FROM notifications WHERE notification_id = $1',
            [notification_id]
        );
        return res.status(200).json(result.rows[0]);
    } catch (error) {
        return res
            .status(500)
            .json(errorHandling.return_error_500(error.message));
    }
});

/* DELETE Notification by ID */
router.delete(
    '/notifications/:notification_id',
    async function (req, res, next) {
        // Validate path param
        const notification_id = req.params.notification_id;
        if (notification_id) {
            let validation_tuple =
                await paramValidation.validate_notification_id(
                    notification_id,
                    paramValidation.ParamType.PATH
                );
            if (!validation_tuple.is_valid) {
                return res
                    .status(404)
                    .json(
                        errorHandling.return_error_404(
                            validation_tuple.error_msg
                        )
                    );
            }
        }

        // Delete row from database
        try {
            const result = await db.query(
                'DELETE FROM notifications WHERE notification_id = $1',
                [notification_id]
            );

            if (result.rowCount === 1) {
                return res.status(204).send();
            } else {
                throw new DatabaseError(
                    'Database delete failed: no rows affected.'
                );
            }
        } catch (error) {
            return res
                .status(500)
                .json(errorHandling.return_error_500(error.message));
        }
    }
);

module.exports = router;
