import varDatabaseDbHandler from "../../backend/varDatabaseDbHandler";
import nodemailer, { SendMailOptions } from "nodemailer";
import http from "http";
import getAuthCookieNames from "../../backend/cookies/get-auth-cookie-names";
import encrypt from "../../dsql/encrypt";
import serializeCookies from "../../../utils/serialize-cookies";
import { SendOneTimeCodeEmailResponse } from "../../../types";

type Param = {
    email: string;
    database: string;
    email_login_field?: string;
    mail_domain?: string;
    mail_port?: number;
    sender?: string;
    mail_username?: string;
    mail_password?: string;
    html: string;
    useLocal?: boolean;
    response?: http.ServerResponse & { [s: string]: any };
    extraCookies?: import("../../../../package-shared/types").CookieObject[];
};

/**
 * # Send Email Login Code
 */
export default async function apiSendEmailCode({
    email,
    database,
    email_login_field,
    mail_domain,
    mail_port,
    sender,
    mail_username,
    mail_password,
    html,
    useLocal,
    response,
    extraCookies,
}: Param): Promise<SendOneTimeCodeEmailResponse> {
    if (email?.match(/ /)) {
        return {
            success: false,
            msg: "Invalid Email/Password format",
        };
    }
    const createdAt = Date.now();

    const foundUserQuery = `SELECT * FROM ${database}.users WHERE email = ?`;
    const foundUserValues = [email];

    let foundUser = await varDatabaseDbHandler({
        queryString: foundUserQuery,
        queryValuesArray: foundUserValues,
        database,
        useLocal,
    });

    ////////////////////////////////////////
    ////////////////////////////////////////
    ////////////////////////////////////////

    if (!foundUser || !foundUser[0]) {
        return {
            success: false,
            msg: "No user found",
        };
    }

    function generateCode() {
        const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        let code = "";
        for (let i = 0; i < 8; i++) {
            code += chars[Math.floor(Math.random() * chars.length)];
        }
        return code;
    }

    if (foundUser?.[0] && email_login_field) {
        const tempCode = generateCode();

        let transporter = nodemailer.createTransport({
            host: mail_domain || process.env.DSQL_MAIL_HOST,
            port: mail_port
                ? mail_port
                : process.env.DSQL_MAIL_PORT
                ? Number(process.env.DSQL_MAIL_PORT)
                : 465,
            secure: true,
            auth: {
                user: mail_username || process.env.DSQL_MAIL_EMAIL,
                pass: mail_password || process.env.DSQL_MAIL_PASSWORD,
            },
        });

        let mailObject: SendMailOptions = {};

        mailObject["from"] = `"Datasquirel SSO" <${
            sender || "support@datasquirel.com"
        }>`;
        mailObject["sender"] = sender || "support@datasquirel.com";
        mailObject["to"] = email;
        mailObject["subject"] = "One Time Login Code";
        mailObject["html"] = html.replace(/{{code}}/, tempCode);

        const info = await transporter.sendMail(mailObject);

        if (!info?.accepted) throw new Error("Mail not Sent!");

        const setTempCodeQuery = `UPDATE ${database}.users SET ${email_login_field} = ? WHERE email = ?`;
        const setTempCodeValues = [tempCode + `-${createdAt}`, email];

        let setTempCode = await varDatabaseDbHandler({
            queryString: setTempCodeQuery,
            queryValuesArray: setTempCodeValues,
            database,
            useLocal,
        });

        /** @type {import("../../../types").SendOneTimeCodeEmailResponse} */
        const resObject: import("../../../types").SendOneTimeCodeEmailResponse =
            {
                success: true,
                code: tempCode,
                email: email,
                createdAt,
                msg: "Success",
            };

        if (response) {
            const cookieKeyNames = getAuthCookieNames();
            const oneTimeCodeCookieName = cookieKeyNames.oneTimeCodeName;

            const encryptedPayload = encrypt({
                data: JSON.stringify(resObject),
            });

            if (!encryptedPayload) {
                throw new Error(
                    "apiSendEmailCode Error: Failed to encrypt payload"
                );
            }

            /** @type {import("../../../../package-shared/types").CookieObject} */
            const oneTimeCookieObject: import("../../../../package-shared/types").CookieObject =
                {
                    name: oneTimeCodeCookieName,
                    value: encryptedPayload,
                    sameSite: "Strict",
                    path: "/",
                    httpOnly: true,
                    secure: true,
                };

            /** @type {import("../../../../package-shared/types").CookieObject[]} */
            const cookiesObjectArray: import("../../../../package-shared/types").CookieObject[] =
                extraCookies
                    ? [...extraCookies, oneTimeCookieObject]
                    : [oneTimeCookieObject];

            const serializedCookies = serializeCookies({
                cookies: cookiesObjectArray,
            });

            response.setHeader("Set-Cookie", serializedCookies);
        }

        return resObject;
    } else {
        return {
            success: false,
            msg: "Invalid Email/Password format",
        };
    }
}