/**
 * Email Notification Service
 * Reusable service untuk mengirim email notification untuk berbagai modul
 * 
 * Features:
 * - Support multiple recipients (to, cc, bcc)
 * - Retry mechanism untuk failed emails
 * - History tracking via HistoryEmail
 * - Support attachments
 * - Configurable email account per company
 */

const nodemailer = require("nodemailer");
const moment = require("moment");
const dbSAPPS = require("../../helpers/db");

/**
 * Get email account configuration for a company
 * @param {Number} company_id - Company ID
 * @returns {Promise<Object|null>} - Email account configuration or null
 */
async function getEmailAccount(company_id) {
	try {
		const emailAccount = await dbSAPPS.EmailAccount.findOne({
			where: {
				company_id: company_id,
			},
			include: [
				{
					model: dbSAPPS.Email,
					as: "email",
					attributes: [
						"id",
						"smtp_server",
						"smtp_port",
						"use_ssl_for_outgoing",
						"use_tls",
						"company_id",
					],
				},
			],
			attributes: ["id", "email_account", "password", "company_id"],
		});

		if (!emailAccount || !emailAccount.email) {
			return null;
		}

		return {
			email_account: emailAccount.email_account,
			password: emailAccount.password,
			smtp_server: emailAccount.email.smtp_server,
			smtp_port: emailAccount.email.smtp_port,
			use_ssl_for_outgoing: emailAccount.email.use_ssl_for_outgoing,
			use_tls: emailAccount.email.use_tls,
		};
	} catch (error) {
		console.error("Error getting email account:", error);
		return null;
	}
}

/**
 * Create email transporter from email account configuration
 * @param {Object} emailConfig - Email account configuration
 * @returns {Object} - Nodemailer transporter
 */
function createTransporter(emailConfig) {
	// Validate email configuration
	if (!emailConfig.smtp_server) {
		throw new Error("SMTP server is required");
	}
	if (!emailConfig.email_account) {
		throw new Error("Email account is required");
	}
	if (!emailConfig.password) {
		throw new Error("Email password is required");
	}
	
	// Create transporter with robust configuration
	const transporterConfig = {
		host: emailConfig.smtp_server,
		port: emailConfig.smtp_port || 465,
		secure: emailConfig.use_ssl_for_outgoing || false,
		auth: {
			user: emailConfig.email_account,
			pass: emailConfig.password,
		},
		tls: {
			rejectUnauthorized: emailConfig.use_tls || false,
		},
		// Add connection timeout
		connectionTimeout: 30000, // 30 seconds
		// Add greeting timeout
		greetingTimeout: 30000, // 30 seconds
		// Add socket timeout
		socketTimeout: 30000, // 30 seconds
	};
	
	return nodemailer.createTransport(transporterConfig);
}

/**
 * Send email notification
 * @param {Object} params - Email parameters
 * @param {Number} params.company_id - Company ID (required)
 * @param {String|Array} params.to - Recipient email(s) (required)
 * @param {String|Array} params.cc - CC email(s) (optional)
 * @param {String|Array} params.bcc - BCC email(s) (optional)
 * @param {String} params.subject - Email subject (required)
 * @param {String} params.html - Email HTML content (required)
 * @param {String} params.text - Email text content (optional)
 * @param {String} params.from - From email address (optional, defaults to email_account)
 * @param {String} params.fromName - From name (optional)
 * @param {Array} params.attachments - Email attachments (optional)
 * @param {Number} params.modified_by - User ID who triggers this (default: 1)
 * @param {Number} params.owner - Owner ID (default: 1)
 * @param {Number} params.notification_id - Notification ID for tracking (optional)
 * @param {Number} params.employee_id - Employee ID for history tracking (optional)
 * @param {Number} params.leaveapplication_id - Leave application ID for history (optional)
 * @param {Number} params.attendancerequest_id - Attendance request ID for history (optional)
 * @param {Number} params.upcountry_id - UpCountry ID for history (optional)
 * @param {Number} params.absenceclarification_id - Absence clarification ID for history (optional)
 * @param {Number} params.permitEase_id - Permit ease ID for history (optional)
 * @param {Number} params.maxRetries - Maximum retry attempts (default: 3)
 * @param {Number} params.retryDelay - Delay between retries in ms (default: 5000)
 * @returns {Promise<Object>} - Result object with success status and message
 */
async function sendEmailNotification(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// Validate required parameters
		if (!params.company_id) {
			await transaction.rollback();
			return {
				success: false,
				message: "company_id is required!",
			};
		}

		if (!params.to || (Array.isArray(params.to) && params.to.length === 0)) {
			await transaction.rollback();
			return {
				success: false,
				message: "Recipient email (to) is required!",
			};
		}

		if (!params.subject) {
			await transaction.rollback();
			return {
				success: false,
				message: "Email subject is required!",
			};
		}

		if (!params.html) {
			await transaction.rollback();
			return {
				success: false,
				message: "Email HTML content is required!",
			};
		}

		// Get email account configuration
		const emailConfig = await getEmailAccount(params.company_id);
		if (!emailConfig) {
			await transaction.rollback();
			return {
				success: false,
				message: `Email account not configured for company ID ${params.company_id}`,
			};
		}

		// Create transporter with validation
		let transporter;
		try {
			transporter = createTransporter(emailConfig);
		} catch (transporterError) {
			await transaction.rollback();
			return {
				success: false,
				message: `Failed to create email transporter: ${transporterError.message}`,
			};
		}

		// Prepare recipients - validate and sanitize email addresses
		const toEmails = Array.isArray(params.to) ? params.to : [params.to];
		const ccEmails = params.cc ? (Array.isArray(params.cc) ? params.cc : [params.cc]) : [];
		const bccEmails = params.bcc ? (Array.isArray(params.bcc) ? params.bcc : [params.bcc]) : [];
		
		// Filter out invalid email addresses
		const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
		const validToEmails = toEmails.filter(email => email && typeof email === 'string' && emailRegex.test(email.trim()));
		const validCcEmails = ccEmails.filter(email => email && typeof email === 'string' && emailRegex.test(email.trim()));
		const validBccEmails = bccEmails.filter(email => email && typeof email === 'string' && emailRegex.test(email.trim()));
		
		if (validToEmails.length === 0) {
			await transaction.rollback();
			return {
				success: false,
				message: "No valid recipient email addresses found!",
			};
		}

		// Prepare from address
		const fromAddress = params.from || emailConfig.email_account;
		const fromName = params.fromName || null;
		const from = fromName ? `${fromName} <${fromAddress}>` : fromAddress;

		// Prepare mail options
		const mailOptions = {
			from: from,
			to: validToEmails.join(", "),
			subject: params.subject,
			html: params.html,
		};

		if (validCcEmails.length > 0) {
			mailOptions.cc = validCcEmails.join(", ");
		}

		if (validBccEmails.length > 0) {
			mailOptions.bcc = validBccEmails.join(", ");
		}

		if (params.text) {
			mailOptions.text = params.text;
		}

		if (params.attachments && Array.isArray(params.attachments) && params.attachments.length > 0) {
			mailOptions.attachments = params.attachments;
		}

		// Send email with retry mechanism and exponential backoff
		const maxRetries = params.maxRetries || 3;
		const baseRetryDelay = params.retryDelay || 5000;
		let lastError = null;
		let emailStatus = "failed";
		let errorMessage = null;

		for (let attempt = 1; attempt <= maxRetries; attempt++) {
			try {
				// Set timeout for email sending (30 seconds)
				const sendEmailPromise = transporter.sendMail(mailOptions);
				const timeoutPromise = new Promise((_, reject) => 
					setTimeout(() => reject(new Error("Email send timeout after 30 seconds")), 30000)
				);
				
				const info = await Promise.race([sendEmailPromise, timeoutPromise]);
				emailStatus = "sent";
				console.log(`Email sent successfully: ${info.messageId} (attempt ${attempt}/${maxRetries})`);
				break;
			} catch (error) {
				lastError = error;
				errorMessage = error.message || "Unknown error";
				console.error(`Email send attempt ${attempt}/${maxRetries} failed:`, errorMessage);

				// Check if error is retryable
				const isRetryable = isRetryableEmailError(error);
				
				if (attempt < maxRetries && isRetryable) {
					// Exponential backoff: delay increases with each retry
					const retryDelay = baseRetryDelay * Math.pow(2, attempt - 1);
					console.log(`Retrying in ${retryDelay}ms... (exponential backoff)`);
					await new Promise((resolve) => setTimeout(resolve, retryDelay));
				} else if (!isRetryable) {
					// Non-retryable error (e.g., invalid email address), stop retrying
					console.error("Non-retryable error detected, stopping retries:", errorMessage);
					break;
				}
			}
		}

		// Create history email for each recipient
		const allRecipients = [...validToEmails, ...validCcEmails, ...validBccEmails];
		const historyEmails = [];

		// Create history email for each recipient with robust error handling
		for (const recipientEmail of allRecipients) {
			try {
				// Truncate HTML body if too long (some databases have TEXT field limits)
				const maxBodyLength = 65535; // MySQL TEXT field max length
				const bodyHtml = params.html && params.html.length > maxBodyLength 
					? params.html.substring(0, maxBodyLength) + "... [truncated]"
					: params.html;
				
				const historyEmail = await dbSAPPS.HistoryEmail.create(
					{
						creation: new Date(),
						modified: new Date(),
						modified_by: params.modified_by || 1,
						owner: params.owner || 1,
						recipient_email: recipientEmail.trim(),
						subject: params.subject ? params.subject.substring(0, 255) : "No Subject",
						body: bodyHtml,
						status: emailStatus,
						error_message: errorMessage ? errorMessage.substring(0, 255) : null,
						// Note: HistoryEmail model only has these module tracking fields:
						// leaveapplication_id, attendancerequest_id, upcountry_id, absenceclarification_id, permitEase_id
						// For PKWT notifications, we track via notification_id in the notification table
						leaveapplication_id: params.leaveapplication_id || null,
						attendancerequest_id: params.attendancerequest_id || null,
						upcountry_id: params.upcountry_id || null,
						absenceclarification_id: params.absenceclarification_id || null,
						permitEase_id: params.permitEase_id || null,
					},
					{ transaction }
				);
				historyEmails.push(historyEmail);
			} catch (error) {
				console.error(`Error creating history email for ${recipientEmail}:`, error);
				// Continue with other recipients even if one fails
			}
		}

		// Update notification status if notification_id is provided
		if (params.notification_id) {
			try {
				const notification = await dbSAPPS.Notification.findByPk(params.notification_id, { transaction });
				if (notification) {
					await notification.update(
						{
							is_sent: emailStatus === "sent",
							sentAt: emailStatus === "sent" ? new Date() : null,
							modified: new Date(),
							modified_by: params.modified_by || notification.modified_by,
						},
						{ transaction }
					);
				} else {
					console.warn(`Notification ${params.notification_id} not found for status update`);
				}
			} catch (error) {
				console.error(`Error updating notification ${params.notification_id}:`, error);
				// Don't fail the entire email sending process if notification update fails
			}
		}

		await transaction.commit();

		if (emailStatus === "sent") {
			return {
				success: true,
				message: `Email sent successfully to ${allRecipients.length} recipient(s)`,
				recipients: allRecipients,
				historyEmails: historyEmails.map((h) => h.id),
			};
		} else {
			return {
				success: false,
				message: `Failed to send email after ${maxRetries} attempts: ${errorMessage}`,
				error: errorMessage,
				recipients: allRecipients,
				historyEmails: historyEmails.map((h) => h.id),
			};
		}
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error?.message || "Failed to send email notification";
		console.error("Error in sendEmailNotification:", error);
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Send email notification for PKWT expiration
 * @param {Object} params - PKWT notification parameters
 * @param {Object} params.employee - Employee object with contract info
 * @param {Number} params.company_id - Company ID
 * @param {Array} params.additionalRecipients - Additional email recipients (optional)
 * @param {Number} params.notification_id - Notification ID (optional)
 * @param {Number} params.modified_by - User ID (default: 1)
 * @param {Number} params.owner - Owner ID (default: 1)
 * @returns {Promise<Object>} - Result object
 */
async function sendPKWTExpirationEmail(params) {
	try {
		if (!params.employee) {
			return {
				success: false,
				message: "Employee data is required!",
			};
		}

		if (!params.company_id) {
			return {
				success: false,
				message: "company_id is required!",
			};
		}

		const employee = params.employee;
		const contractEndDate = employee.contract_end_date || null;
		const daysUntilExpiration = params.daysUntilExpiration || null;

		// Load email template
		const emailTemplate = require("./emailTemplates/email.pkwtExpiration");

		// Prepare recipients
		const recipients = [];
		if (employee.email) {
			recipients.push(employee.email);
		}

		// Add additional recipients
		if (params.additionalRecipients && Array.isArray(params.additionalRecipients)) {
			params.additionalRecipients.forEach((email) => {
				if (email && typeof email === "string" && email.trim() && !recipients.includes(email.trim())) {
					recipients.push(email.trim());
				}
			});
		}

		if (recipients.length === 0) {
			return {
				success: false,
				message: "No valid email recipients found!",
			};
		}

		// Prepare email data
		const emailData = {
			employee_name: employee.employee_name || "Employee",
			employee_number: employee.employee_number || "N/A",
			contract_end_date: contractEndDate,
			days_until_expiration: daysUntilExpiration,
			company_name: employee.company?.display_name || employee.company?.name || "Company",
		};

		// Generate HTML from template
		const html = emailTemplate.html(emailData);

		// Prepare subject
		const subject = `Pemberitahuan: Kontrak PKWT ${employee.employee_name} Akan Berakhir${daysUntilExpiration !== null ? ` (${daysUntilExpiration} hari lagi)` : ""}`;

		// Send email
		const result = await sendEmailNotification({
			company_id: params.company_id,
			to: recipients,
			subject: subject,
			html: html,
			fromName: "SAPPS Notification System",
			notification_id: params.notification_id || null,
			employee_id: employee.id || null,
			modified_by: params.modified_by || 1,
			owner: params.owner || 1,
		});

		return result;
	} catch (error) {
		console.error("Error in sendPKWTExpirationEmail:", error);
		return {
			success: false,
			message: error?.message || "Failed to send PKWT expiration email",
		};
	}
}

/**
 * Send email notification for Absence Clarification
 * @param {Object} params - Absence clarification notification parameters
 * @param {Object} params.absenceClarification - Absence clarification object
 * @param {String} params.status - Status: "Need Approval", "Approved", "Rejected", "Revised", "Cancelled"
 * @param {String} params.website - Website URL for action buttons
 * @param {String} params.recipientEmail - Recipient email address (approver or employee)
 * @param {Number} params.company_id - Company ID
 * @param {Number} params.modified_by - User ID (default: 1)
 * @param {Number} params.owner - Owner ID (default: 1)
 * @returns {Promise<Object>} - Result object
 */
async function sendAbsenceClarificationEmail(params) {
	try {
		if (!params.absenceClarification) {
			return {
				success: false,
				message: "Absence clarification data is required!",
			};
		}

		if (!params.company_id) {
			return {
				success: false,
				message: "company_id is required!",
			};
		}

		if (!params.status) {
			return {
				success: false,
				message: "Status is required!",
			};
		}

		if (!params.recipientEmail) {
			return {
				success: false,
				message: "Recipient email is required!",
			};
		}

		const absenceClarification = params.absenceClarification;
		const status = params.status;
		const website = params.website || "sapps.sapharma.co.id";

		// Load appropriate email template based on status
		let emailTemplate;
		let subjectPrefix = "";

		try {
			switch (status) {
				case "Need Approval":
					emailTemplate = require("../../absenceClarifications/emailTemplates/email.needapproval");
					subjectPrefix = "Absence Clarification Need Approval";
					break;
				case "Approved":
					emailTemplate = require("../../absenceClarifications/emailTemplates/email.approved");
					subjectPrefix = "Absence Clarification Approved";
					break;
				case "Rejected":
					emailTemplate = require("../../absenceClarifications/emailTemplates/email.rejected");
					subjectPrefix = "Absence Clarification Rejected";
					break;
				case "Revised":
					emailTemplate = require("../../absenceClarifications/emailTemplates/email.revised");
					subjectPrefix = "Absence Clarification Revision";
					break;
				case "Cancelled":
					emailTemplate = require("../../absenceClarifications/emailTemplates/email.cancelled");
					subjectPrefix = "Absence Clarification Cancelled";
					break;
				default:
					return {
						success: false,
						message: `Unsupported status: ${status}`,
					};
			}
		} catch (error) {
			console.error("Error loading email template:", error);
			return {
				success: false,
				message: `Failed to load email template for status: ${status}`,
			};
		}

		// Generate HTML from template with error handling
		let html;
		try {
			html = emailTemplate.html(absenceClarification, website);
			if (!html || typeof html !== 'string') {
				throw new Error("Email template returned invalid HTML");
			}
		} catch (templateError) {
			console.error("Error generating email template:", templateError);
			return {
				success: false,
				message: `Failed to generate email template: ${templateError.message}`,
			};
		}

		// Prepare subject with validation
		const employeeName = absenceClarification.employee_name || absenceClarification.employee?.employee_name || "Employee";
		const periodeInfo = absenceClarification.periode_month && absenceClarification.periode_year
			? `${absenceClarification.periode_month}/${absenceClarification.periode_year}`
			: "N/A";
		const subject = `${subjectPrefix} (${employeeName} - ${periodeInfo})`;

		// Prepare attachments if filename exists
		const attachments = [];
		// Note: AbsenceClarification doesn't have filename field, but we can add it if needed

		// Send email
		const result = await sendEmailNotification({
			company_id: params.company_id,
			to: params.recipientEmail,
			subject: subject,
			html: html,
			fromName: "SAPPS Notification System",
			absenceclarification_id: absenceClarification.id || null,
			employee_id: absenceClarification.employee_id || null,
			modified_by: params.modified_by || 1,
			owner: params.owner || 1,
			attachments: attachments.length > 0 ? attachments : undefined,
		});

		return result;
	} catch (error) {
		console.error("Error in sendAbsenceClarificationEmail:", error);
		return {
			success: false,
			message: error?.message || "Failed to send absence clarification email",
		};
	}
}

/**
 * Send email notification for Permit Ease
 * @param {Object} params - Permit ease notification parameters
 * @param {Object} params.permitEase - Permit ease object
 * @param {String} params.status - Status: "Need Approval", "Approved", "Rejected", "Revised", "Cancelled"
 * @param {String} params.website - Website URL for action buttons
 * @param {String} params.recipientEmail - Recipient email address (approver or employee)
 * @param {Number} params.company_id - Company ID
 * @param {Number} params.modified_by - User ID (default: 1)
 * @param {Number} params.owner - Owner ID (default: 1)
 * @returns {Promise<Object>} - Result object
 */
async function sendPermitEaseEmail(params) {
	try {
		if (!params.permitEase) {
			return {
				success: false,
				message: "Permit ease data is required!",
			};
		}

		if (!params.company_id) {
			return {
				success: false,
				message: "company_id is required!",
			};
		}

		if (!params.status) {
			return {
				success: false,
				message: "Status is required!",
			};
		}

		if (!params.recipientEmail) {
			return {
				success: false,
				message: "Recipient email is required!",
			};
		}

		const permitEase = params.permitEase;
		const status = params.status;
		const website = params.website || "sapps.sapharma.co.id";

		// Load appropriate email template based on status
		let emailTemplate;
		let subjectPrefix = "";

		try {
			switch (status) {
				case "Need Approval":
					emailTemplate = require("../../permitEases/emailTemplates/email.needapproval");
					subjectPrefix = "Permit Ease Need Approval";
					break;
				case "Approved":
					emailTemplate = require("../../permitEases/emailTemplates/email.approved");
					subjectPrefix = "Permit Ease Approved";
					break;
				case "Rejected":
					emailTemplate = require("../../permitEases/emailTemplates/email.rejected");
					subjectPrefix = "Permit Ease Rejected";
					break;
				case "Revised":
					emailTemplate = require("../../permitEases/emailTemplates/email.revised");
					subjectPrefix = "Permit Ease Revision";
					break;
				case "Cancelled":
					emailTemplate = require("../../permitEases/emailTemplates/email.cancelled");
					subjectPrefix = "Permit Ease Cancelled";
					break;
				default:
					return {
						success: false,
						message: `Unsupported status: ${status}`,
					};
			}
		} catch (error) {
			console.error("Error loading email template:", error);
			return {
				success: false,
				message: `Failed to load email template for status: ${status}`,
			};
		}

		// Generate HTML from template with error handling
		let html;
		try {
			html = emailTemplate.html(permitEase, website);
			if (!html || typeof html !== 'string') {
				throw new Error("Email template returned invalid HTML");
			}
		} catch (templateError) {
			console.error("Error generating email template:", templateError);
			return {
				success: false,
				message: `Failed to generate email template: ${templateError.message}`,
			};
		}

		// Prepare subject with validation
		const employeeName = permitEase.employee_name || permitEase.employee?.employee_name || "Employee";
		let dateRange = "N/A";
		try {
			if (permitEase.half_day && permitEase.half_day_date) {
				dateRange = moment(permitEase.half_day_date).format("YYYY-MM-DD");
			} else if (permitEase.from_date && permitEase.to_date) {
				dateRange = `${moment(permitEase.from_date).format("YYYY-MM-DD")} to ${moment(permitEase.to_date).format("YYYY-MM-DD")}`;
			}
		} catch (dateError) {
			console.warn("Error formatting date range for permit ease email:", dateError);
			dateRange = "N/A";
		}
		const subject = `${subjectPrefix} (${employeeName} - ${dateRange})`;

		// Prepare attachments if filename exists
		const attachments = [];
		if (permitEase.filename) {
			try {
				const fs = require('fs');
				const path = require('path');
				const attachmentPath = path.join(__dirname, '../../../public/data/documents/permitEases', permitEase.filename);
				
				// Check if file exists before adding as attachment
				if (fs.existsSync(attachmentPath)) {
					attachments.push({
						filename: permitEase.filename,
						path: attachmentPath,
					});
				} else {
					console.warn(`Attachment file not found: ${attachmentPath}`);
				}
			} catch (attachmentError) {
				console.warn("Error preparing attachment:", attachmentError);
				// Continue without attachment if file access fails
			}
		}

		// Send email
		const result = await sendEmailNotification({
			company_id: params.company_id,
			to: params.recipientEmail,
			subject: subject,
			html: html,
			fromName: "SAPPS Notification System",
			permitEase_id: permitEase.id || null,
			employee_id: permitEase.employee_id || null,
			modified_by: params.modified_by || 1,
			owner: params.owner || 1,
			attachments: attachments.length > 0 ? attachments : undefined,
		});

		return result;
	} catch (error) {
		console.error("Error in sendPermitEaseEmail:", error);
		return {
			success: false,
			message: error?.message || "Failed to send permit ease email",
		};
	}
}

/**
 * Check if email error is retryable
 * @param {Error} error - Error object
 * @returns {Boolean} - True if error is retryable
 */
function isRetryableEmailError(error) {
	if (!error) return false;
	
	const errorMessage = (error.message || "").toLowerCase();
	const errorCode = error.code || "";
	
	// Retryable errors: network issues, timeouts, server errors
	const retryablePatterns = [
		"timeout",
		"econnrefused",
		"econnreset",
		"enotfound",
		"ehostunreach",
		"etimedout",
		"esocket",
		"550", // SMTP error codes that are retryable
		"451", // Temporary failure
		"452", // Insufficient storage
		"552", // Exceeded storage allocation
	];
	
	// Non-retryable errors: invalid email, authentication errors, etc.
	const nonRetryablePatterns = [
		"invalid",
		"authentication",
		"auth",
		"unauthorized",
		"550 5.1.1", // Invalid recipient
		"550 5.1.3", // Invalid address
		"553", // Mailbox name not allowed
	];
	
	// Check for non-retryable patterns first
	for (const pattern of nonRetryablePatterns) {
		if (errorMessage.includes(pattern) || errorCode.includes(pattern)) {
			return false;
		}
	}
	
	// Check for retryable patterns
	for (const pattern of retryablePatterns) {
		if (errorMessage.includes(pattern) || errorCode.includes(pattern)) {
			return true;
		}
	}
	
	// Default: retry for unknown errors (might be temporary)
	return true;
}

module.exports = {
	sendEmailNotification,
	sendPKWTExpirationEmail,
	sendAbsenceClarificationEmail,
	sendPermitEaseEmail,
	getEmailAccount,
	createTransporter,
	isRetryableEmailError,
};
