// API endpoint to process email user actions - urls we send in emails to users

import { getToken } from "next-auth/jwt";
import type { NextApiRequest, NextApiResponse } from 'next';
import { createRouter, expressWrapper } from "next-connect";
const common = require('../../src/helpers/common');
const data = require('../../src/helpers/data');
const emailHelper = require('../../src/helpers/email');
const { v4: uuidv4 } = require('uuid');
const CON = require("../../src/helpers/const");
import { EventLogType } from "@prisma/client";
const logger = require('../../src/logger');

import fs from 'fs';
import path from 'path';
import { log } from "console";
const handlebars = require("handlebars");

const router = createRouter<NextApiRequest, NextApiResponse>();


// programatically sign in
import { getSession } from "next-auth/react";
import { signIn } from "next-auth/react";

import { authOptions } from './auth/[...nextauth]';
// import NextAuth from 'next-auth';
// import { setCookie } from 'nookies';

//action to accept coverme request from email


/**
 * 
 * @param req import { NextApiRequest, NextApiResponse } from 'next'
 * @param res import { NextApiRequest, NextApiResponse } from 'next'
 */
export default async function handler(req, res) {
    const prisma = common.getPrismaClient();

    const action = req.query.action;
    const emailaction = req.query.emailaction;
    // Retrieve and validate the JWT token

    let email = req.body.email || req.query.email;
    //response is a special action that does not require a token
    //PUBLIC 
    if (action == "email_response" || action == "account") {
        switch (emailaction) {
            case "validateEmail":
                let token = req.query.token;
                let user = await prisma.user.findUnique({
                    where: {
                        email: email
                    }
                });
                let pub = await prisma.publisher.findUnique({
                    where: {
                        email: email
                    }
                });
                if (!user && !pub) {
                    return res.status(400).json({ message: "Invalid user" });
                }
                if (user) {
                    await prisma.user.update({
                        where: {
                            email: email
                        },
                        data: {
                            emailVerified: new Date()
                        }
                    });
                }
                logger.info("User: " + email + " validated his email.");
                console.log("User: " + email + " validated his email. Logging in...");
                return res.redirect("/dash");
            // log in the user using nextauth and redirect to the dashboard
            //how to login the user with nextauth? maybe use the signIn callback
            // const result = await signIn("credentials", {
            //     // redirect: false,
            //     email,
            //     account: user
            // });

            // if (result.error) {
            //     return res.status(401).json({ message: "Invalid credentials" });
            // }

            // return res.status(200).json({ message: "Signed in successfully" });


            case "coverMeAccept":
                //validate shiftId and assignmentId
                let shiftId = req.query.shiftId;
                let userId = req.query.userId;
                let publisher = await prisma.publisher.findUnique({
                    where: {
                        id: userId
                    }
                });
                // Update the user status to accepted
                console.log("User: " + publisher.firstName + " " + publisher.lastName + " accepted the CoverMe request");
                logger.info("" + publisher.firstName + " " + publisher.lastName + " accepted the CoverMe request for shift " + shiftId + " PID: " + req.query.assignmentPID + "");

                let assignmentPID = req.query.assignmentPID;
                if (!shiftId) {
                    return res.status(400).json({ message: "Shift ID is not provided" });
                }
                if (!assignmentPID) {
                    return res.status(400).json({ message: "Assignment PID is not provided" });
                }
                //check if the assignment request is still open
                const assignment = await prisma.assignment.findFirst({
                    where: {
                        publicGuid: assignmentPID,
                        shiftId: parseInt(shiftId),
                        isConfirmed: false
                    },
                    include: {
                        shift: {
                            include: {
                                cartEvent: {
                                    include: {
                                        location: true
                                    }
                                },
                                assignments: {
                                    include: {
                                        publisher: true
                                        // {
                                        // include: {
                                        //     email: true,
                                        //     firstName: true,
                                        //     lastName: true
                                        // }
                                        // }
                                    }
                                }
                            }
                        },
                        publisher: true
                    }
                });

                if (!assignment) {
                    const messagePageUrl = `/message?message=${encodeURIComponent('Благодаря за желанието, но някой е отговорил на тази заявка за заместване и тя вече е неактивна')}&type=info&caption=${encodeURIComponent('Някой вече те изпревари. Заявката е вече обработена')}`;
                    res.redirect(messagePageUrl);
                    return;
                }
                let originalPublisher = assignment.publisher;
                let to = assignment.shift.assignments.map(a => a.publisher.email);
                to.push(publisher.email);

                // update the assignment. clear the guid, isConfirmed to true
                await prisma.assignment.update({
                    where: {
                        id: assignment.id
                    },
                    data: {
                        publisherId: userId,
                        originalPublisherId: originalPublisher.id,
                        publicGuid: null, // if this exists, we consider the request open
                        isConfirmed: true
                    }
                });
                const newAssignment = await prisma.assignment.findFirst({
                    where: {
                        shiftId: parseInt(shiftId),
                        isConfirmed: true
                    },
                    include: {
                        shift: {
                            include: {
                                cartEvent: {
                                    include: {
                                        location: true
                                    }
                                },
                                assignments: {
                                    include: {
                                        publisher: true
                                    }
                                }
                            }
                        }
                    }
                });
                await prisma.eventLog.create({
                    data: {
                        date: new Date(),
                        publisher: { connect: { id: publisher.id } },
                        shift: { connect: { id: assignment.shiftId } },
                        type: EventLogType.AssignmentReplacementAccepted,
                        content: `Заявката за заместване на ${originalPublisher.firstName} ${originalPublisher.lastName} е приета от ${publisher.firstName} ${publisher.lastName}`
                    }
                });


                const shiftStr = `${CON.weekdaysBG[assignment.shift.startTime.getDay()]} ${CON.GetDateFormat(assignment.shift.startTime)} at ${assignment.shift.cartEvent.location.name} from ${CON.GetTimeFormat(assignment.shift.startTime)} to ${CON.GetTimeFormat(assignment.shift.endTime)}`;

                const newPubs = newAssignment.shift.assignments.map(a => ({
                    name: `${a.publisher.firstName} ${a.publisher.lastName}`,
                    phone: a.publisher.phone
                }));

                let model = {
                    user: publisher,
                    shiftStr: shiftStr,
                    shiftId: assignment.shiftId,
                    prefix: publisher.isMale ? "Брат" : "Сестра",
                    oldPubName: assignment.publisher.firstName + " " + assignment.publisher.lastName,
                    firstName: publisher.firstName,
                    lastName: publisher.lastName,
                    newPubs: newPubs,
                    placeName: assignment.shift.cartEvent.location.name,
                    dateStr: common.getDateFormated(assignment.shift.startTime),
                    time: common.getTimeFormatted(assignment.shift.startTime),
                    sentDate: common.getDateFormated(new Date())
                };

                emailHelper.SendEmailHandlebars(to, "coverMeAccepted", model);

                const messagePageUrl = `/message?message=${encodeURIComponent('Вашата заявка за замстване е обработена успешно')}&type=info&caption=${encodeURIComponent('Благодаря!')}`;
                res.redirect(messagePageUrl);

                break;

            //POST
            case "send_report": //we can send report form in the emails to the user. process the POSTED data here
                // Send report form to the user
                //get from POST data: locationId, date, placementCount, videoCount, returnVisitInfoCount, conversationCount
                let locationId = req.body.locationId;
                let date = req.body.date;
                let placementCount = req.body.placementCount;
                let videoCount = req.body.videoCount;
                let returnVisitInfoCount = req.body.returnVisitInfoCount;
                let conversationCount = req.body.conversationCount;

                console.log("User: " + user.email + " sent a report: " +
                    locationId + " " + date + " " +
                    placementCount + " " + videoCount + " " +
                    returnVisitInfoCount + " " + conversationCount);

                //save the report in the database
                await prisma.report.create({
                    data: {
                        userId: parseInt(userId),
                        locationId: parseInt(locationId),
                        date: date,
                        placementCount: parseInt(placementCount),
                        videoCount: parseInt(videoCount),
                        returnVisitInfoCount: parseInt(returnVisitInfoCount),
                        conversationCount: parseInt(conversationCount)
                    }
                });

                break;

            case "resetPassword":
                // Send password reset form to the user
                //parse the request body

                let actualUser = await prisma.publisher.findUnique({
                    where: {
                        email: email
                    }
                });
                if (!actualUser) {
                    return res.status(200).json({ message: "Няма потребител с този имейл" });
                }
                else {
                    let requestGuid = req.query.guid;
                    if (!requestGuid) {
                        console.log("User: " + email + " requested a password reset");
                        let requestGuid = uuidv4();
                        //save the request in the database as EventLog
                        let eventLog = await prisma.eventLog.create({
                            data: {
                                date: new Date(),
                                publisher: { connect: { id: actualUser.id } },
                                type: EventLogType.PasswordResetRequested,
                                content: JSON.stringify({ guid: requestGuid })
                            }
                        });
                        logger.info("User: " + email + " requested a password reset. EventLogId: " + eventLog.id + "");

                        let model = {
                            email: email,
                            firstName: actualUser.firstName,
                            lastName: actualUser.lastName,
                            resetUrl: process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=resetPassword&guid=" + requestGuid + "&email=" + email,
                            sentDate: common.getDateFormated(new Date())
                        };
                        emailHelper.SendEmailHandlebars(to, "resetPassword", model);
                        res.status(200).json({ message: "Password reset request sent" });
                    }
                    else {
                        //1. validate the guid
                        let eventLog = await prisma.eventLog.findFirst({
                            where: {//can we query "{ guid: requestGuid }"?
                                type: EventLogType.PasswordResetRequested,
                                publisherId: actualUser.id,
                                date: {
                                    gt: new Date(new Date().getTime() - 24 * 60 * 60 * 1000) //24 hours
                                }
                            }
                        });

                        if (!eventLog) {
                            return res.status(400).json({ message: "Invalid or expired password reset request" });
                        }
                        else {
                            let eventLog = await prisma.eventLog.update({
                                where: {
                                    id: parseInt(requestGuid)
                                },
                                data: {
                                    type: EventLogType.PasswordResetEmailConfirmed
                                }
                            });
                            //2. redirect to the password reset page
                            const messagePageUrl = `/auth/reset-password?email=${email}&resetToken=${requestGuid}`;
                            res.redirect(messagePageUrl);
                        }

                        //2.login the user

                        //3. redirect to the password reset page
                    }

                }
                break;

        }
        // //send email response to the user
        // const emailResponse = await common.sendEmail(user.email, "Email Action Processed",
        //     "Your email action was processed successfully");
    }
    // ########################
    //       PRIVATE API
    // ########################
    else {

        const token = await getToken({ req: req });
        if (!token) {
            // If no token or invalid token, return unauthorized status
            return res.status(401).json({ message: "Unauthorized to call this API endpoint" });
        }

        const publisher = await prisma.publisher.findUnique({
            where: {
                email: token.email
            }
        });

        //PRIVATE ACTIONS
        switch (action) {
            //in nextauth.ts
            // case "validateEmail":
            //     let publisher = await prisma.publisher.findUnique({
            //         where: {
            //             email: token.email
            //         }
            //     });
            //     if (!publisher) {
            //         return res.status(400).json({ message: "Invalid user" });
            //     }

            case "sendCoverMeRequestByEmail":
                // Send CoverMe request to the users
                //get from POST data: shiftId, assignmentId, date
                //let shiftId = req.body.shiftId;
                let assignmentId = req.body.assignmentId;

                let toSubscribed = req.body.toSubscribed;
                let toAvailable = req.body.toAvailable;

                let assignment = await prisma.assignment.findUnique({
                    where: {
                        id: parseInt(assignmentId)
                    },
                    include: {
                        shift: {
                            include: {
                                cartEvent: {
                                    include: {
                                        location: true
                                    }
                                }
                            }
                        }
                    }
                });

                // update the assignment. generate new publicGuid, isConfirmed to false
                let newPublicGuid = uuidv4();
                await prisma.assignment.update({
                    where: {
                        id: parseInt(assignmentId)
                    },
                    data: {
                        publicGuid: newPublicGuid, // if this exists, we consider the request open
                        isConfirmed: false
                    }
                });

                let targetEmails = await data.getCoverMePublisherEmails(assignment.shift.id);
                if (!toSubscribed) {
                    targetEmails.subscribedPublishers = [];
                }
                if (!toAvailable) {
                    targetEmails.availablePublishers = [];
                }

                //concat and remove duplicate emails
                let pubsToSend = targetEmails.subscribedPublishers.concat(targetEmails.availablePublishers).
                    filter((item, index, self) =>
                        index === self.findIndex((t) => (
                            t.email === item.email && item.email !== publisher.email//and exclude the user himself
                        ))
                    );
                console.log("Sending CoverMe request to " + pubsToSend.length + " publishers");

                let eventLog = await prisma.eventLog.create({
                    data: {
                        date: new Date(),
                        publisher: { connect: { id: publisher.id } },
                        shift: { connect: { id: assignment.shift.id } },
                        type: EventLogType.AssignmentReplacementRequested,
                        content: "Заявка за заместване от " + publisher.firstName + " " + publisher.lastName
                            + " до: " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", "),
                    }
                });
                logger.info(". EventLogId: " + eventLog.id + "User: " + publisher.email + " sent a 'CoverMe' request for his assignment " + assignmentId + ", shift " + assignment.shift.id + " - " + assignment.shift.cartEvent.location.name + " " + assignment.shift.startTime.toISOString() + " to " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", "));

                //send email to all subscribed publishers
                for (let i = 0; i < pubsToSend.length; i++) {

                    //send email to subscribed publisher
                    let acceptUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=coverMeAccept&userId=" + pubsToSend[i].id + "&shiftId=" + assignment.shift.id + "&assignmentPID=" + newPublicGuid + "&eventLogID=" + eventLog.id;
                    publisher.prefix = publisher.isMale ? "Брат" : "Сестра";

                    let model = {
                        user: publisher,
                        shiftId: assignment.shift.id,
                        acceptUrl: acceptUrl,
                        firstName: pubsToSend[i].firstName,
                        lastName: pubsToSend[i].lastName,
                        email: pubsToSend[i].email,
                        placeName: assignment.shift.cartEvent.location.name,
                        dateStr: common.getDateFormated(assignment.shift.startTime),
                        time: common.getTimeFormatted(assignment.shift.startTime),
                        sentDate: common.getDateFormated(new Date())
                    };
                    let results = emailHelper.SendEmailHandlebars(
                        {
                            name: pubsToSend[i].firstName + " " + pubsToSend[i].lastName,
                            email: pubsToSend[i].email
                        }, "coverMe", model);
                    // if (results) {
                    //     console.log("Error sending email: " + error);
                    //     return res.status(500).json({ message: "Error sending email:" + error });
                    //}

                    if (results) {
                        console.log("Email sent to: " + pubsToSend[i].email);
                    }

                }
                res.status(200).json({ message: "CoverMe request sent" });

                break;
            default:
                return res.status(400).json({ message: "Invalid action" });
        }

    }

}
router.use(expressWrapper(handler));

