const Log = require('./log');
const Info = require('./info');
const db = require('../dbconn');
var Mailer = function (obj) { };
const nodemailer = require('nodemailer');
const { convert } = require('xmlbuilder2');
const axios = require('axios');
fs = require('fs');
const connectionPools = {};

function getTransporter(outgoing_user_name, outgoing_password, mail_server) {
    if (!connectionPools[outgoing_user_name]) {
        // Create a new transporter if it doesn't exist
        connectionPools[outgoing_user_name] = nodemailer.createTransport({
            // service: 'gmail',
            host: mail_server,
            auth: {
                user: outgoing_user_name,
                pass: outgoing_password
            },
            tls: {
                rejectUnauthorized: false
            }
        });
    }
    return connectionPools[outgoing_user_name];
}

Mailer.sendAutoReply = function (data) {
    if (!data || data.length === 0) {
        Log.outErrorLog('No data provided for sending auto-reply.');
        return Promise.resolve({ status: false, message: 'No data provided for sending auto-reply.' });
    }

    const emailPromises = [];
    for (let i = 0; i < data.length; i++) {
        const currentData = data[i];

        if (!currentData.to_email) {
            updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id)
            Log.outErrorLog(`Invalid Mail Data: Missing to_email. ${JSON.stringify(currentData)}`);
            continue;
        }

        Log.outErrorLog({ msg: `sending mail process on ${currentData.from_email} start here from mail id is ${currentData.outgoing_user_name} to mail id is ${currentData.to_email} and conversation id ${currentData.conversation_id}` });

        if (currentData.server_type === "" || currentData.server_type == null) {
            updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id)
            Log.outErrorLog(`Invalid Mail Data: server_type is required. conversation id is  ${currentData.conversation_id}`);
            continue;
        }

        if (currentData.server_type == "Pop3" || currentData.server_type == "IMap") {
            // Log.outErrorLog(`currentdata ${JSON.stringify(currentData)}`);

            if (currentData.outgoing_user_name != "" && (currentData.outgoing_password != "" || currentData.outgoing_is_auth_enable == 1) && currentData.outgoing_mail_server != "" && currentData.outgoing_mail_server_port != "" && currentData.stop_outgoing_email == 0) {
                
                if (currentData.outgoing_is_auth_enable == 1) {                    
                    const mailOptions = {
                        to: currentData.to_email,
                        cc: currentData.cc_email,
                        bcc: currentData.bcc_email,
                        subject: currentData.subject,
                        html: currentData.conversation,
                        headers: {
                            'Message-ID': currentData.email_message_id
                        },
                    };

                    const emailPromise = sendViaGraph(currentData, mailOptions)
                        .then(() => {
                            Log.outErrorLog(`Graph API: Email sent to ${currentData.to_email}`);
                            let updArr = { email_status: "Send" };
                            let whArr = { conversation_id: currentData.conversation_id };
                            let mstTblName = `tms_conversation_master_${currentData.queue_id}`;
                            return db.updateData(mstTblName, updArr, whArr);
                        })
                        .catch((error) => {
                            updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id);
                            Log.outErrorLog(`Graph API: Error sending email to ${currentData.to_email}: ${error.message}`);
                            return { status: false, message: error.message };
                        });

                    emailPromises.push(emailPromise);
                    continue; 
                  
                } else {
                    // Standard SMTP authentication
                    transporter = getTransporter(
                        currentData.is_aws == 1 ? currentData.user_for_aws : currentData.outgoing_user_name,
                        currentData.outgoing_password,
                        currentData.outgoing_mail_server
                    );

                    Log.outErrorLog(`SMTP transporter created for ${currentData.outgoing_user_name} on server ${currentData.outgoing_mail_server}`);
                }

                const mailOptions = {
                    from: currentData.outgoing_user_name,
                    to: currentData.to_email,
                    cc: currentData.cc_email,
                    bcc: currentData.bcc_email,
                    subject: currentData.subject,
                    html: currentData.conversation,
                    headers: {
                        'Message-ID': currentData.email_message_id
                    },
                };

                if (currentData.refrence_id && currentData.refrence_id !== "") {
                    mailOptions.headers['References'] = [currentData.refrence_id];
                }

                if (currentData.attachmentArray && currentData.attachmentArray.length > 0) {
                    mailOptions.attachments = mailOptions.attachments || [];
                    mailOptions.attachments = mailOptions.attachments.concat(
                        currentData.attachmentArray.map(attachment => ({
                            filename: attachment.file_name,
                            path: attachment.file_url
                        }))
                    );
                }

                if (currentData.footer_name != "" && (currentData.footer_url != null || currentData.footer_url != "")) {
                    mailOptions.attachments = mailOptions.attachments || [];
                    mailOptions.attachments.push({
                        filename: currentData.footer_name,
                        path: `/var/www/html/tms/api/uploads/queuefooter/queuefooter_${currentData.queue_id}/${currentData.footer_name}`,
                        cid: 'footer'
                    });
                }

                // --------block email for autoreply validation start here---------------
                if (currentData.conversation_type !== "reply" && currentData.conversation_type !== "forward" && currentData.conversation_type !== "escalation matrix") {
                    Log.outErrorLog(`"block_email_for_auto_reply",${currentData.block_email_for_auto_reply}`);
                    if (currentData.block_email_for_auto_reply != "") {
                        const blockEmailIds = currentData.block_email_for_auto_reply.split(',');
                        Log.outErrorLog(`"blockEmailIds",${blockEmailIds}`);
                        const matchedEmails = [];
                        const shouldSendEmail = !blockEmailIds.some(email => {
                            const emailFields = ['to'];
                            const matched = emailFields.some(field => mailOptions[field] && mailOptions[field].includes(email));
                            if (matched) {
                                matchedEmails.push(email);
                            }
                            return matched;
                        });
                        Log.outErrorLog(`${shouldSendEmail}`);
                        if (!shouldSendEmail) {
                            emailPromises.push(new Promise((resolve, reject) => {
                                let updArr = {
                                    'email_status': 'None'
                                };
                                let whArr = {
                                    'conversation_id': currentData.conversation_id
                                };
                                let mstTblName = `tms_conversation_master_${currentData.queue_id}`;
                                db.updateData(`${mstTblName}`, updArr, whArr)
                                    .then(function (response) {
                                        Log.outErrorLog({ msg: `${JSON.stringify(response)}` });
                                        if (response.status == true) {
                                            resolve({ status: true });
                                        } else {
                                            Log.outErrorLog({ error: response.err });
                                            reject({ status: false, data: null, err: response.err });
                                        }
                                    })
                                    .catch(function (error) {
                                        Log.outErrorLog({ error: error });
                                        reject({ status: false, data: null, err: error });
                                    });
                                Log.outErrorLog(`Email blocked due to block_email_for_auto_reply value ${matchedEmails.join(', ')}`);
                                resolve({ status: false, data: null, message: `Email blocked due to block_email_for_auto_reply value ${matchedEmails.join(', ')}` });
                            }));
                            continue;
                        }
                    }

                    // -----------block domain for autoreply validation start here--------------
                    if (currentData.block_domains_for_auto_reply != "") {
                        const blockDomains = currentData.block_domains_for_auto_reply.split(',');
                        Log.outErrorLog(`${blockDomains}`);
                        const matchedDomains = [];
                        const shouldSendBlockedDomainEmail = !blockDomains.some(domain => {
                            if (mailOptions.to && mailOptions.to.includes(domain)) {
                                matchedDomains.push(domain);
                                return true;
                            }
                            return false;
                        });

                        if (!shouldSendBlockedDomainEmail) {
                            emailPromises.push(new Promise((resolve, reject) => {
                                let updArr = {
                                    'email_status': 'None'
                                };
                                let whArr = {
                                    'conversation_id': currentData.conversation_id
                                };
                                let mstTblName = `tms_conversation_master_${currentData.queue_id}`;
                                db.updateData(`${mstTblName}`, updArr, whArr)
                                    .then(function (response) {
                                        Log.outErrorLog({ msg: `${JSON.stringify(response)}` });
                                        if (response.status == true) {
                                            resolve({ status: true });
                                        } else {
                                            Log.outErrorLog({ error: response.err });
                                            reject({ status: false, data: null, err: response.err });
                                        }
                                    })
                                    .catch(function (error) {
                                        Log.outErrorLog({ error: error });
                                        reject({ status: false, data: null, err: error });
                                    });
                                Log.outErrorLog(`Email blocked due to block_domains_for_ticket_generate value ${matchedDomains.join(', ')}`);
                                resolve({ status: false, data: null, message: `Email blocked due to block_domains_for_ticket_generate value ${matchedDomains.join(', ')}` });
                            }));
                            continue;
                        }
                    }
                }

                const emailPromise = transporter.sendMail(mailOptions)
                    .then((info) => {
                        Log.outErrorLog(`Email sent to ${currentData.to_email}: ${info.response}`);

                        let updArr = {
                            'email_status': 'Send'
                        };
                        let whArr = {
                            'conversation_id': currentData.conversation_id
                        };
                        let mstTblName = `tms_conversation_master_${currentData.queue_id}`;
                        return db.updateData(`${mstTblName}`, updArr, whArr)
                            .then(function (response) {
                                Log.outErrorLog({ msg: `${JSON.stringify(response)}` });
                                if (response.status == true) {
                                    return { status: true };
                                } else {
                                    Log.outErrorLog({ error: response.err });
                                    return { status: false, data: null, err: response.err };
                                }
                            })
                            .catch(function (error) {
                                Log.outErrorLog({ error: error });
                                return { status: false, data: null, err: error };
                            });
                    })
                    .catch((error) => {
                        updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id)
                        Log.outErrorLog(`Error sending email to ${currentData.to_email}: ${error.message}`);
                        return { status: false, message: `Error sending email to ${currentData.to_email}.` };
                    });

                emailPromises.push(emailPromise);
            } else {
                updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id)
                Log.outErrorLog({ msg: "Outgoing server details are required" });
                continue;
            }
        } else {
            updateNoOfTry(currentData.conversation_id, currentData.no_of_try, currentData.queue_id)
            Log.outErrorLog({ msg: "Exchange Not implemented" });
            continue;
        }
    }
    return Promise.all(emailPromises)
        .then((results) => {
            const allEmailsSent = results.every(result => result.status === true);
            if (allEmailsSent) {
                return { status: true, message: 'All emails sent successfully.' };
            } else {
                return { status: false, message: 'Error sending one or more emails.' };
            }
        })
        .finally(() => {
            Object.values(connectionPools).forEach(pool => pool.close());
        });
}

function updateNoOfTry(conversation_id, no_of_try, queueId) {

    let noOfTry = parseInt((no_of_try == null) ? 0 : no_of_try) + 1;
    let updStrArr = {
        'no_of_try': noOfTry
    }
    let whArr = {
        'conversation_id': conversation_id
    };
    let mstTblName = `tms_conversation_master_${queueId}`;
    return db.updateData(`${mstTblName}`, updStrArr, whArr)
        .then(function (response) {
            Log.outErrorLog({ msg: `${JSON.stringify(response)}` });
            if (response.status == true) {
                return { status: true };
            } else {
                Log.outErrorLog({ error: response.err });
                return { status: false, data: null, err: response.err };
            }
        })
        .catch(function (error) {
            Log.outErrorLog({ error: error });
            return { status: false, data: null, err: error };
        });

}

Mailer.SendBulkEmail = async function (data, outgoingData, queueId) {
    const transporter = getTransporter(outgoingData.outgoing_user_name, outgoingData.outgoing_password);
    const mailOptions = {
        from: data.from_email,
        to: data.to_emails,
        subject: data.subject,
        html: data.conversation,
    };

    const emailPromises = [];
    const emailPromise = transporter.sendMail(mailOptions)
        .then((info) => {
            Log.outErrorLog(`Email sent to ${data.to_email}: ${info.response}`);

            let updArr = {
                'email_status': 'Send'
            };
            let whArr = {
                'id': data.id
            };
            let mstTblName = `bulk_escalation_data_${queueId}`;
            db.updateData(`${mstTblName}`, updArr, whArr)
                .then(function (response) {
                    Log.outErrorLog({ msg: `${JSON.stringify(response)}` });
                    if (response.status == true) {
                        return { status: true };
                    } else {
                        Log.outErrorLog({ error: response.err });
                        return { status: false, data: null, err: response.err };
                    }
                })
                .catch(function (error) {
                    Log.outErrorLog({ error: error });
                    return { status: false, data: null, err: error };
                });
            return { status: true, message: `Email sent to ${data.to_emails} successfully.` };
        })
        .catch((error) => {
            Log.outErrorLog(`Error sending email to ${data.to_emails}: ${error.message}`);
            return { status: false, message: `Error sending email to ${data.to_emails}.` };
        });
    emailPromises.push(emailPromise);
    const results = await Promise.all(emailPromises);
    const allEmailsSent = results.every(result => result.status === true);
    if (allEmailsSent) {
        return { status: true, message: 'All emails sent successfully.' };
    } else {
        return { status: false, message: 'Error sending one or more emails.' };
    }
}

async function sendViaGraph(currentData, mailOptions) {
    const url = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(currentData.outgoing_user_name)}/sendMail`;

    const graphPayload = {
        message: {
            subject: mailOptions.subject,
            body: {
                contentType: "HTML",
                content: `${mailOptions.html}` || "<p>Thank you for your email. We'll get back to you shortly.</p>"
            },
            toRecipients: mailOptions.to?.split(",").map(email => ({
                emailAddress: { address: email.trim() }
            })) || [],
            ccRecipients: mailOptions.cc
                ? mailOptions.cc.split(",").map(email => ({
                    emailAddress: { address: email.trim() }
                }))
                : [],
            bccRecipients: mailOptions.bcc
                ? mailOptions.bcc.split(",").map(email => ({
                    emailAddress: { address: email.trim() }
                }))
                : [],
            internetMessageHeaders: []
        },
        saveToSentItems: true
    };

    // Add pseudo headers for references/message-id
    if (currentData.email_message_id) {
        graphPayload.message.internetMessageHeaders.push(
            { name: "x-message-id", value: currentData.email_message_id }
        );
    }
    if (currentData.refrence_id) {
        graphPayload.message.internetMessageHeaders.push(
            { name: "x-references", value: currentData.refrence_id }
        );
    }

    // Handle attachments (Graph requires base64 contentBytes)
    if (currentData.attachmentArray && currentData.attachmentArray.length > 0) {
        graphPayload.message.attachments = await Promise.all(
            currentData.attachmentArray.map(async (attachment) => {
                const fileContent = await fs.promises.readFile(attachment.file_url); // read file
                return {
                    "@odata.type": "#microsoft.graph.fileAttachment",
                    name: attachment.file_name,
                    contentBytes: fileContent.toString("base64")
                };
            })
        );
    }

    // Handle footer attachment
    if (currentData.footer_name && currentData.footer_name !== "") {
        const footerPath = `/var/www/html/tms/api/uploads/queuefooter/queuefooter_${currentData.queue_id}/${currentData.footer_name}`;
        try {
            const footerContent = await fs.promises.readFile(footerPath);
            graphPayload.message.attachments = graphPayload.message.attachments || [];
            graphPayload.message.attachments.push({
                "@odata.type": "#microsoft.graph.fileAttachment",
                name: currentData.footer_name,
                contentId: "footer", // for inline use in HTML
                isInline: true,
                contentBytes: footerContent.toString("base64")
            });
        } catch (err) {
            Log.outErrorLog(`⚠️ Footer file not found: ${footerPath}`);
        }
    }

    async function postEmail(token) {
        return axios.post(url, graphPayload, {
            headers: {
                Authorization: `Bearer ${token}`,
                "Content-Type": "application/json"
            }
        });
    }

    try {
        await postEmail(currentData.outgoing_token);
    } catch (err) {
        if (err.response) {
            const status = err.response.status;
            if (status === 401) {
                Log.outErrorLog(`🔄 Token expired for ${currentData.outgoing_user_name}, refreshing...`);
                const newToken = await refreshGraphToken(currentData);
                await postEmail(newToken);
            } else if (status === 429) {
                const retryAfter = parseInt(err.response.headers["retry-after"], 10) || 5;
                Log.outErrorLog(`⚠️ Rate limit hit. Retrying in ${retryAfter} seconds...`);
                await new Promise(res => setTimeout(res, retryAfter * 1000));
                await postEmail(currentData.outgoing_token);
            } else {
                Log.outErrorLog(`❌ Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
                throw new Error(`Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
            }
        } else {
            throw err;
        }
    }
}

// async function sendViaGraph(currentData, mailOptions) {
//     const url = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(currentData.outgoing_user_name)}/sendMail`;

//     const graphPayload = {
//         message: {
//             subject: mailOptions.subject,
//             body: {
//                 contentType: "HTML",
//                 content: `${mailOptions.html}` || "<p>Thank you for your email. We'll get back to you shortly.</p>"
//             },
//             toRecipients: mailOptions.to?.split(",").map(email => ({
//                 emailAddress: { address: email.trim() }
//             })) || [],
//             ccRecipients: mailOptions.cc
//                 ? mailOptions.cc.split(",").map(email => ({
//                     emailAddress: { address: email.trim() }
//                 }))
//                 : [],
//             bccRecipients: mailOptions.bcc
//                 ? mailOptions.bcc.split(",").map(email => ({
//                     emailAddress: { address: email.trim() }
//                 }))
//                 : [],
//             internetMessageHeaders: currentData.email_message_id
//                 ? [
//                     { name: "In-Reply-To", value: currentData.email_message_id },
//                     { name: "References", value: currentData.email_message_id }
//                 ]
//                 : []
//         },
//         saveToSentItems: true
//     };
//     // const graphPayload = {
//     //     "message": {
//     //       "subject": "Test Email from API",
//     //       "body": {
//     //         "contentType": "html",
//     //         "content": "Hello, this is <p>a</p> test email."
//     //       },
//     //       "toRecipients": [
//     //         {
//     //           "emailAddress": {
//     //             "address": "ranjan.sharma@omniscienttech.me"
//     //           }
//     //         }
//     //       ]
//     //     }
//     // }
//     const xmlPayload = convert(graphPayload, { format: "object" });
//     Log.outErrorLog("graphPayload: " + JSON.stringify(xmlPayload));

//     async function postEmail(token) {
//         return axios.post(url, xmlPayload, {
//             headers: {
//                 Authorization: `Bearer ${token}`,
//                 "Content-Type": "application/json"
//             }
//         });
//     }

//     try {
//         await postEmail(currentData.outgoing_token);
//     } catch (err) {
//         if (err.response) {
//             const status = err.response.status;

//             if (status === 401) {
//                 Log.outErrorLog(`🔄 Token expired for ${currentData.outgoing_user_name}, refreshing...`);
//                 const newToken = await refreshGraphToken(currentData);
//                 console.log(newToken,"newToken")
//                 await postEmail(newToken);
//             } else if (status === 429) {
//                 const retryAfter = parseInt(err.response.headers["retry-after"], 10) || 5;
//                 Log.outErrorLog(`⚠️ Rate limit hit. Retrying in ${retryAfter} seconds...`);
//                 await new Promise(res => setTimeout(res, retryAfter * 1000));
//                 await postEmail(currentData.outgoing_token);
//             } else {
//                 Log.outErrorLog(`❌ Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
//                 throw new Error(`Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
//             }
//         } else {
//             throw err;
//         }
//     }
// }

// async function sendViaGraph(currentData, mailOptions) {
//     const url = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(currentData.outgoing_user_name)}/sendMail`;

//     const graphPayload = {
//         message: {
//             subject: mailOptions.subject,
//             body: {
//                 contentType: "HTML",
//                 content: `${mailOptions.html}` || "<p>Thank you for your email. We'll get back to you shortly.</p>"
//             },
//             toRecipients: mailOptions.to?.split(",").map(email => ({
//                 emailAddress: { address: email.trim() }
//             })) || [],
//             ccRecipients: mailOptions.cc
//                 ? mailOptions.cc.split(",").map(email => ({
//                     emailAddress: { address: email.trim() }
//                 }))
//                 : [],
//             bccRecipients: mailOptions.bcc
//                 ? mailOptions.bcc.split(",").map(email => ({
//                     emailAddress: { address: email.trim() }
//                 }))
//                 : [],            
//             internetMessageHeaders: currentData.email_message_id
//                 ? [
//                     { name: "x-inreplyto", value: currentData.email_message_id },
//                     { name: "x-references", value: currentData.email_message_id }
//                 ]
//                 : []
//         },
//         saveToSentItems: true
//     };
    

//     async function postEmail(token) {
//         return axios.post(url, graphPayload, {
//             headers: {
//                 Authorization: `Bearer ${token}`,
//                 "Content-Type": "application/json"
//             }
//         });
//     }

//     try {
//         await postEmail(currentData.outgoing_token);
//     } catch (err) {
//         if (err.response) {
//             const status = err.response.status;
//             if (status === 401) {
//                 Log.outErrorLog(`🔄 Token expired for ${currentData.outgoing_user_name}, refreshing...`);
//                 const newToken = await refreshGraphToken(currentData);
//                 await postEmail(newToken);
//             } else if (status === 429) {
//                 const retryAfter = parseInt(err.response.headers["retry-after"], 10) || 5;
//                 Log.outErrorLog(`⚠️ Rate limit hit. Retrying in ${retryAfter} seconds...`);
//                 await new Promise(res => setTimeout(res, retryAfter * 1000));
//                 await postEmail(currentData.outgoing_token);
//             } else {
//                 Log.outErrorLog(`❌ Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
//                 throw new Error(`Graph API Error ${status}: ${JSON.stringify(err.response.data)}`);
//             }
//         } else {
//             throw err;
//         }
//     }
// }






async function refreshGraphToken(currentData) {
    const tokenUrl = `https://login.microsoftonline.com/${currentData.outgoing_tenant_id}/oauth2/v2.0/token`;

    const params = new URLSearchParams({
        client_id: currentData.outgoing_client_oauth2_id,
        client_secret: currentData.outgoing_client_secret,
        refresh_token: currentData.outgoing_refresh_token,
        grant_type: "refresh_token",
        scope: "offline_access https://graph.microsoft.com/Mail.Send https://graph.microsoft.com/Mail.ReadWrite"
    });

    try {
        const response = await axios.post(tokenUrl, params, {
            headers: { "Content-Type": "application/x-www-form-urlencoded" }
        });

        const newAccessToken = response.data.access_token;
        const newRefreshToken = response.data.refresh_token || currentData.outgoing_refresh_token;

        // Update DB so we don't have to refresh every time
        await db.updateData("tms_email_account", {
            outgoing_token: newAccessToken,
            outgoing_refresh_token: newRefreshToken
        }, { id: currentData.email_account_id });

        Log.outErrorLog(`✅ Refreshed token for ${currentData.outgoing_user_name}`);
        return newAccessToken;
    } catch (err) {
        Log.outErrorLog(`❌ Failed to refresh token: ${err.message}`);
        throw err;
    }
}


module.exports = Mailer;