"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.emailService = void 0;
const nodemailer_1 = __importDefault(require("nodemailer"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const env_1 = require("../config/env");
const logger_1 = require("../utils/logger");
const settings_service_1 = require("../modules/settings/settings.service");
const client_1 = require("@prisma/client");
const prisma = new client_1.PrismaClient();
// In-memory template cache (filesystem)
const templateCache = new Map();
const databaseTemplateCache = new Map();
class EmailService {
    transporter;
    constructor() {
        this.transporter = this.createTransporter(env_1.env.SMTP_HOST, env_1.env.SMTP_PORT, env_1.env.SMTP_USER, env_1.env.SMTP_PASS);
    }
    createTransporter(host, port, user, pass) {
        const config = { host, port };
        // Only set auth if user is provided (MailHog doesn't need auth)
        if (user) {
            config.auth = { user, pass };
        }
        return nodemailer_1.default.createTransport(config);
    }
    /** Rebuild transporter from DB settings (with env fallback). Call after SMTP settings change. */
    async rebuildTransporter() {
        try {
            const settings = await settings_service_1.siteSettingsService.get();
            const provider = settings.smtpActiveProvider || 'mailhog';
            let host, port, user, pass;
            if (provider === 'mailhog') {
                host = 'mailhog-changemaker';
                port = 1025;
                user = '';
                pass = '';
            }
            else {
                host = settings.smtpHost || env_1.env.SMTP_HOST;
                port = settings.smtpPort || env_1.env.SMTP_PORT;
                user = settings.smtpUser || env_1.env.SMTP_USER;
                pass = settings.smtpPass || env_1.env.SMTP_PASS;
            }
            this.transporter = this.createTransporter(host, port, user, pass);
            logger_1.logger.info(`SMTP transporter rebuilt: provider=${provider} ${host}:${port} (auth=${!!user})`);
        }
        catch (err) {
            logger_1.logger.error('Failed to rebuild SMTP transporter, keeping current config:', err);
        }
    }
    async testConnection() {
        try {
            await this.transporter.verify();
            logger_1.logger.info('SMTP connection verified');
            return true;
        }
        catch (err) {
            logger_1.logger.error('SMTP connection failed:', err);
            return false;
        }
    }
    /**
     * Load template from database (with caching)
     */
    async loadTemplateFromDatabase(key) {
        // Check cache first
        const cached = databaseTemplateCache.get(key);
        if (cached) {
            return cached;
        }
        try {
            const template = await prisma.emailTemplate.findUnique({
                where: { key, isActive: true },
            });
            if (!template) {
                return null;
            }
            const dbTemplate = {
                html: template.htmlContent,
                text: template.textContent,
                subject: template.subjectLine,
            };
            // Cache for future use
            databaseTemplateCache.set(key, dbTemplate);
            logger_1.logger.debug(`Loaded template "${key}" from database`);
            return dbTemplate;
        }
        catch (error) {
            logger_1.logger.error(`Failed to load template "${key}" from database:`, error);
            return null;
        }
    }
    /**
     * Load template - checks database first, falls back to filesystem
     * @param name Template key (e.g., "campaign-email")
     * @param type File type (html or txt) - only used for filesystem fallback
     */
    loadTemplate(name, type) {
        const key = `${name}.${type}`;
        const cached = templateCache.get(key);
        if (cached)
            return cached;
        // Filesystem loading (legacy fallback)
        const templatePath = path.join(process.cwd(), 'src', 'templates', 'email', `${name}.${type}`);
        const content = fs.readFileSync(templatePath, 'utf-8');
        templateCache.set(key, content);
        logger_1.logger.warn(`Template "${name}" loaded from filesystem (not in database)`);
        return content;
    }
    /**
     * Clear database template cache
     * @param key Optional specific template key to clear. If not provided, clears all.
     */
    clearDatabaseCache(key) {
        if (key) {
            databaseTemplateCache.delete(key);
            logger_1.logger.debug(`Cleared database cache for template: ${key}`);
        }
        else {
            databaseTemplateCache.clear();
            logger_1.logger.debug('Cleared all database template cache');
        }
    }
    escapeHtml(unsafe) {
        return unsafe
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#x27;');
    }
    /**
     * Fetch video metadata from Media API
     */
    async getVideoMetadata(videoId) {
        try {
            const mediaApiUrl = env_1.env.MEDIA_API_PUBLIC_URL || 'http://media-api:4100';
            const response = await fetch(`${mediaApiUrl}/api/videos/${videoId}/metadata`);
            if (!response.ok)
                return null;
            return await response.json();
        }
        catch (error) {
            logger_1.logger.error(`Failed to fetch video ${videoId} metadata:`, error);
            return null;
        }
    }
    formatDuration(seconds) {
        if (!seconds || seconds <= 0)
            return '0:00';
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        if (h > 0)
            return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
        return `${m}:${String(s).padStart(2, '0')}`;
    }
    formatViewCount(count) {
        if (!count || count <= 0)
            return '0 views';
        if (count === 1)
            return '1 view';
        if (count < 1000)
            return `${count} views`;
        if (count < 1_000_000)
            return `${(count / 1000).toFixed(1).replace(/\.0$/, '')}K views`;
        return `${(count / 1_000_000).toFixed(1).replace(/\.0$/, '')}M views`;
    }
    async processTemplate(template, vars, variableDefinitions) {
        let result = template;
        // Process VIDEO variables first (async)
        if (variableDefinitions) {
            for (const varDef of variableDefinitions) {
                if (varDef.type === 'VIDEO' && varDef.videoId) {
                    const video = await this.getVideoMetadata(varDef.videoId);
                    if (video) {
                        const publicUrl = env_1.env.ADMIN_URL || 'http://localhost:3000';
                        const watchUrl = `${publicUrl}/gallery/watch/${video.id}`;
                        const duration = this.formatDuration(video.durationSeconds || 0);
                        const quality = video.quality ? this.escapeHtml(video.quality) : '';
                        const views = this.formatViewCount(video.viewCount || 0);
                        const metaLine = [duration, quality, views].filter(Boolean).join(' &bull; ');
                        const videoHtml = `
              <table cellpadding="0" cellspacing="0" border="0" style="max-width: 480px; margin: 16px auto; border-radius: 8px; overflow: hidden; background-color: #1b2838;">
                <tr>
                  <td style="padding: 0;">
                    <a href="${watchUrl}" style="display: block; text-decoration: none;">
                      <img src="${video.thumbnailUrl}"
                           alt="${this.escapeHtml(video.title)}"
                           width="480"
                           style="width: 100%; max-width: 480px; height: auto; display: block;" />
                    </a>
                  </td>
                </tr>
                <tr>
                  <td style="padding: 12px 16px;">
                    <table cellpadding="0" cellspacing="0" border="0" width="100%">
                      <tr>
                        <td style="color: #ffffff; font-size: 15px; font-weight: 600; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
                          ${this.escapeHtml(video.title)}
                        </td>
                      </tr>
                      <tr>
                        <td style="padding-top: 6px; color: #8899aa; font-size: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
                          ${metaLine}
                        </td>
                      </tr>
                      <tr>
                        <td style="padding-top: 12px; text-align: center;">
                          <a href="${watchUrl}"
                             style="display: inline-block; padding: 10px 24px; background-color: #9d4edd; color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 14px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
                            &#9654; Watch Video
                          </a>
                        </td>
                      </tr>
                    </table>
                  </td>
                </tr>
              </table>
            `.trim();
                        result = result.replace(new RegExp(`\\{\\{${varDef.key}\\}\\}`, 'g'), videoHtml);
                    }
                    else {
                        // Fallback if video not found
                        result = result.replace(new RegExp(`\\{\\{${varDef.key}\\}\\}`, 'g'), `<p style="color: #999; font-style: italic;">[Video not available]</p>`);
                    }
                }
            }
        }
        // Handle {{#if VAR}}...{{/if}} conditionals
        result = result.replace(/\{\{#if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_match, varName, content) => {
            return vars[varName] ? content : '';
        });
        // Handle TEXT {{VAR}} replacements with HTML escaping
        for (const [key, value] of Object.entries(vars)) {
            const escapedValue = this.escapeHtml(value);
            result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), escapedValue);
        }
        return result;
    }
    /**
     * Process text template (for plain text emails)
     */
    async processTextTemplate(template, vars, variableDefinitions) {
        let result = template;
        // VIDEO variables in text emails: plain text link
        if (variableDefinitions) {
            for (const varDef of variableDefinitions) {
                if (varDef.type === 'VIDEO' && varDef.videoId) {
                    const video = await this.getVideoMetadata(varDef.videoId);
                    if (video) {
                        const publicUrl = env_1.env.ADMIN_URL || 'http://localhost:3000';
                        const duration = this.formatDuration(video.durationSeconds || 0);
                        const quality = video.quality || '';
                        const views = this.formatViewCount(video.viewCount || 0);
                        const meta = [duration, quality, views].filter(Boolean).join(', ');
                        const textLink = `Watch "${video.title}" (${meta})\n${publicUrl}/gallery/watch/${video.id}`;
                        result = result.replace(new RegExp(`\\{\\{${varDef.key}\\}\\}`, 'g'), textLink);
                    }
                    else {
                        result = result.replace(new RegExp(`\\{\\{${varDef.key}\\}\\}`, 'g'), '[Video not available]');
                    }
                }
            }
        }
        // Process TEXT variables (no HTML escaping in plain text)
        for (const [key, value] of Object.entries(vars)) {
            result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
        }
        return result;
    }
    /**
     * Process subject line template
     * Same as processTemplate but without HTML escaping
     */
    processSubject(subject, vars) {
        let result = subject;
        // Handle {{VAR}} replacements (no HTML escaping for subject lines)
        for (const [key, value] of Object.entries(vars)) {
            result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
        }
        return result;
    }
    async getFromField() {
        try {
            const settings = await settings_service_1.siteSettingsService.get();
            const name = settings.emailFromName || env_1.env.SMTP_FROM_NAME;
            const address = settings.smtpFromAddress || env_1.env.SMTP_FROM;
            return `${name} <${address}>`;
        }
        catch {
            return `${env_1.env.SMTP_FROM_NAME} <${env_1.env.SMTP_FROM}>`;
        }
    }
    async getOrganizationName() {
        try {
            const settings = await settings_service_1.siteSettingsService.get();
            return settings.organizationName || 'Changemaker Lite';
        }
        catch {
            return 'Changemaker Lite';
        }
    }
    async getEmailTestConfig() {
        try {
            const settings = await settings_service_1.siteSettingsService.get();
            return {
                testMode: settings.emailTestMode,
                testRecipient: settings.testEmailRecipient || env_1.env.TEST_EMAIL_RECIPIENT,
            };
        }
        catch {
            return {
                testMode: env_1.env.EMAIL_TEST_MODE === 'true',
                testRecipient: env_1.env.TEST_EMAIL_RECIPIENT,
            };
        }
    }
    async sendEmail(options) {
        const { testMode, testRecipient } = await this.getEmailTestConfig();
        const from = await this.getFromField();
        let to = options.to;
        let subject = options.subject;
        if (testMode) {
            subject = `[TEST - Original: ${options.to}] ${options.subject}`;
            to = testRecipient;
        }
        try {
            const info = await this.transporter.sendMail({
                from,
                to,
                subject,
                replyTo: options.replyTo,
                html: options.html,
                text: options.text,
            });
            logger_1.logger.info(`Email sent: ${info.messageId} to=${to} testMode=${testMode}`);
            return {
                success: true,
                messageId: info.messageId,
                testMode,
            };
        }
        catch (err) {
            logger_1.logger.error('Failed to send email:', err);
            return {
                success: false,
                testMode,
            };
        }
    }
    async sendCampaignEmail(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            CAMPAIGN_TITLE: options.campaignTitle,
            MESSAGE: options.message,
            USER_NAME: options.userName,
            USER_EMAIL: options.userEmail,
            POSTAL_CODE: options.postalCode,
            RECIPIENT_NAME: options.recipientName || '',
            RECIPIENT_LEVEL: options.recipientLevel || '',
            ORGANIZATION_NAME: orgName,
            TIMESTAMP: new Date().toISOString(),
        };
        // Try database first
        const dbTemplate = await this.loadTemplateFromDatabase('campaign-email');
        let html, text, subject;
        if (dbTemplate) {
            // Use database template
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
            logger_1.logger.debug('Using campaign email template from database');
        }
        else {
            // Fallback to filesystem
            const htmlTemplate = this.loadTemplate('campaign-email', 'html');
            const txtTemplate = this.loadTemplate('campaign-email', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTemplate(txtTemplate, vars);
            subject = options.subject; // Use provided subject for filesystem fallback
            logger_1.logger.warn('Using campaign email template from filesystem (fallback)');
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            replyTo: options.userEmail,
            html,
            text,
        });
    }
    /** Check whether production SMTP is configured (not mailhog test mode) */
    async isSmtpConfigured() {
        try {
            const settings = await settings_service_1.siteSettingsService.get();
            return settings.smtpActiveProvider === 'production' && !!settings.smtpHost;
        }
        catch {
            return env_1.env.SMTP_HOST !== 'mailhog-changemaker' && !!env_1.env.SMTP_HOST;
        }
    }
    async sendVerificationEmail(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            RECIPIENT_NAME: options.recipientName || 'there',
            VERIFICATION_URL: options.verificationUrl,
            ORGANIZATION_NAME: orgName,
            EXPIRY_HOURS: '24',
        };
        const dbTemplate = await this.loadTemplateFromDatabase('email-verification');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('email-verification', 'html');
            const txtTemplate = this.loadTemplate('email-verification', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Verify your email — ${orgName}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    async sendPasswordResetEmail(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            RECIPIENT_NAME: options.recipientName || 'there',
            RESET_URL: options.resetUrl,
            ORGANIZATION_NAME: orgName,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('password-reset');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('password-reset', 'html');
            const txtTemplate = this.loadTemplate('password-reset', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Reset your password — ${orgName}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    async sendAccountApprovedEmail(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            RECIPIENT_NAME: options.recipientName || 'there',
            LOGIN_URL: options.loginUrl,
            ORGANIZATION_NAME: orgName,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('account-approved');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('account-approved', 'html');
            const txtTemplate = this.loadTemplate('account-approved', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Account approved — ${orgName}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    async sendPendingApprovalNotification(options) {
        const orgName = await this.getOrganizationName();
        const adminUrl = env_1.env.ADMIN_URL || 'http://localhost:3000';
        const vars = {
            NEW_USER_NAME: options.newUserName || '(not provided)',
            NEW_USER_EMAIL: options.newUserEmail,
            REGISTERED_AT: new Date().toLocaleDateString('en-CA', { year: 'numeric', month: 'long', day: 'numeric' }),
            ADMIN_URL: `${adminUrl}/app/users?status=PENDING_APPROVAL`,
            ORGANIZATION_NAME: orgName,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('account-pending-approval');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('account-pending-approval', 'html');
            const txtTemplate = this.loadTemplate('account-pending-approval', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `New user awaiting approval — ${orgName}`;
        }
        for (const email of options.adminEmails) {
            await this.sendEmail({
                to: email,
                subject,
                html,
                text,
            });
        }
    }
    async sendShiftSignupConfirmation(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            USER_NAME: options.recipientName,
            USER_EMAIL: options.recipientEmail,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            SHIFT_TIME: options.shiftTime,
            SHIFT_LOCATION: options.shiftLocation,
            IS_NEW_USER: options.isNewUser ? 'true' : '',
            TEMP_PASSWORD: options.tempPassword || '',
            LOGIN_URL: options.loginUrl,
            ORGANIZATION_NAME: orgName,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('shift-signup-confirmation');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('shift-signup-confirmation', 'html');
            const txtTemplate = this.loadTemplate('shift-signup-confirmation', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Signup Confirmed — ${options.shiftTitle}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    async sendShiftDetailsEmail(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            USER_NAME: options.recipientName,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            SHIFT_START_TIME: options.shiftStartTime,
            SHIFT_END_TIME: options.shiftEndTime,
            SHIFT_LOCATION: options.shiftLocation,
            SHIFT_DESCRIPTION: options.shiftDescription,
            CURRENT_VOLUNTEERS: options.currentVolunteers.toString(),
            MAX_VOLUNTEERS: options.maxVolunteers.toString(),
            SHIFT_STATUS: options.shiftStatus,
            ORGANIZATION_NAME: orgName,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('shift-details');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('shift-details', 'html');
            const txtTemplate = this.loadTemplate('shift-details', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Shift Details — ${options.shiftTitle}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    // ─── Notification Emails ────────────────────────────────────────────
    async sendAdminShiftSignupAlert(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            VOLUNTEER_NAME: options.volunteerName,
            VOLUNTEER_EMAIL: options.volunteerEmail,
            SIGNUP_SOURCE: options.signupSource,
            ADMIN_URL: options.adminUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('admin-shift-signup-alert');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('admin-shift-signup-alert', 'html');
            const txtTemplate = this.loadTemplate('admin-shift-signup-alert', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `New shift signup — ${options.shiftTitle}`;
        }
        for (const email of options.adminEmails) {
            await this.sendEmail({ to: email, subject, html, text });
        }
    }
    async sendAdminShiftCancellationAlert(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            VOLUNTEER_NAME: options.volunteerName,
            VOLUNTEER_EMAIL: options.volunteerEmail,
            CANCELLATION_SOURCE: options.cancellationSource,
            ADMIN_URL: options.adminUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('admin-shift-cancellation-alert');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('admin-shift-cancellation-alert', 'html');
            const txtTemplate = this.loadTemplate('admin-shift-cancellation-alert', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Shift cancellation — ${options.shiftTitle}`;
        }
        for (const email of options.adminEmails) {
            await this.sendEmail({ to: email, subject, html, text });
        }
    }
    async sendAdminResponseSubmittedAlert(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            CAMPAIGN_TITLE: options.campaignTitle,
            REPRESENTATIVE_NAME: options.representativeName,
            RESPONSE_TYPE: options.responseType.replace(/_/g, ' '),
            SUBMITTER_NAME: options.submitterName,
            ADMIN_URL: options.adminUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('admin-response-submitted-alert');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('admin-response-submitted-alert', 'html');
            const txtTemplate = this.loadTemplate('admin-response-submitted-alert', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `New response submitted — ${options.campaignTitle}`;
        }
        for (const email of options.adminEmails) {
            await this.sendEmail({ to: email, subject, html, text });
        }
    }
    async sendAdminSignRequestedAlert(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            VOLUNTEER_NAME: options.volunteerName,
            ADDRESS: options.address,
            SHIFT_TITLE: options.shiftTitle,
            SIGN_SIZE: options.signSize || 'Not specified',
            ADMIN_URL: options.adminUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('admin-sign-requested-alert');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('admin-sign-requested-alert', 'html');
            const txtTemplate = this.loadTemplate('admin-sign-requested-alert', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Sign requested — ${options.address}`;
        }
        for (const email of options.adminEmails) {
            await this.sendEmail({ to: email, subject, html, text });
        }
    }
    async sendVolunteerSessionSummary(options) {
        const orgName = await this.getOrganizationName();
        // Build outcome breakdown as HTML table and plain text list
        const outcomeEntries = Object.entries(options.outcomeBreakdown);
        let outcomeHtml = '';
        let outcomeText = '';
        if (outcomeEntries.length > 0) {
            outcomeHtml = '<table class="outcome-table"><tr><th>Outcome</th><th>Count</th></tr>';
            outcomeText = 'Outcome Breakdown:\n';
            for (const [outcome, count] of outcomeEntries) {
                const label = outcome.replace(/_/g, ' ');
                outcomeHtml += `<tr><td>${this.escapeHtml(label)}</td><td>${count}</td></tr>`;
                outcomeText += `  ${label}: ${count}\n`;
            }
            outcomeHtml += '</table>';
        }
        const vars = {
            ORGANIZATION_NAME: orgName,
            VOLUNTEER_NAME: options.volunteerName,
            CUT_NAME: options.cutName,
            SESSION_DATE: options.sessionDate,
            VISIT_COUNT: options.visitCount.toString(),
            DURATION_MINUTES: options.durationMinutes.toString(),
            DISTANCE_KM: options.distanceKm.toFixed(1),
            OUTCOME_BREAKDOWN: outcomeHtml,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('volunteer-session-summary');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            // Use plain text breakdown for text version
            vars.OUTCOME_BREAKDOWN = outcomeText;
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('volunteer-session-summary', 'html');
            const txtTemplate = this.loadTemplate('volunteer-session-summary', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            vars.OUTCOME_BREAKDOWN = outcomeText;
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Canvass session summary — ${options.cutName}`;
        }
        await this.sendEmail({ to: options.volunteerEmail, subject, html, text });
    }
    async sendVolunteerCancellationAck(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            VOLUNTEER_NAME: options.volunteerName,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            SHIFT_TIME: options.shiftTime,
            SIGNUP_URL: options.signupUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('volunteer-cancellation-ack');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('volunteer-cancellation-ack', 'html');
            const txtTemplate = this.loadTemplate('volunteer-cancellation-ack', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Signup cancelled — ${options.shiftTitle}`;
        }
        await this.sendEmail({ to: options.volunteerEmail, subject, html, text });
    }
    async sendVolunteerShiftThankYou(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            VOLUNTEER_NAME: options.volunteerName,
            SHIFT_TITLE: options.shiftTitle,
            SHIFT_DATE: options.shiftDate,
            SHIFT_TIME: options.shiftTime,
            SHIFT_LOCATION: options.shiftLocation,
            SIGNUP_URL: options.signupUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('volunteer-shift-thank-you');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('volunteer-shift-thank-you', 'html');
            const txtTemplate = this.loadTemplate('volunteer-shift-thank-you', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Thank you for volunteering — ${options.shiftTitle}`;
        }
        await this.sendEmail({ to: options.volunteerEmail, subject, html, text });
    }
    async sendVolunteerReengagement(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            ORGANIZATION_NAME: orgName,
            VOLUNTEER_NAME: options.volunteerName,
            LAST_ACTIVITY_DATE: options.lastActivityDate,
            LAST_ACTIVITY_TYPE: options.lastActivityType,
            SIGNUP_URL: options.signupUrl,
        };
        const dbTemplate = await this.loadTemplateFromDatabase('volunteer-reengagement');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('volunteer-reengagement', 'html');
            const txtTemplate = this.loadTemplate('volunteer-reengagement', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `We miss you — ${orgName}`;
        }
        await this.sendEmail({ to: options.volunteerEmail, subject, html, text });
    }
    async sendResponseVerification(options) {
        const orgName = await this.getOrganizationName();
        const vars = {
            CAMPAIGN_TITLE: options.campaignTitle,
            RESPONSE_TYPE: options.responseType.replace(/_/g, ' '),
            RESPONSE_TEXT: options.responseText.length > 500
                ? options.responseText.slice(0, 500) + '...'
                : options.responseText,
            SUBMITTER_NAME: options.submitterName,
            SUBMITTED_DATE: new Date().toLocaleDateString('en-CA', { year: 'numeric', month: 'long', day: 'numeric' }),
            VERIFICATION_URL: options.verificationUrl,
            REPORT_URL: options.reportUrl,
            ORGANIZATION_NAME: orgName,
            TIMESTAMP: new Date().toISOString(),
        };
        // Try database first
        const dbTemplate = await this.loadTemplateFromDatabase('response-verification');
        let html, text, subject;
        if (dbTemplate) {
            // Use database template
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
            logger_1.logger.debug('Using response verification template from database');
        }
        else {
            // Fallback to filesystem
            const htmlTemplate = this.loadTemplate('response-verification', 'html');
            const txtTemplate = this.loadTemplate('response-verification', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTemplate(txtTemplate, vars);
            subject = `Verify Response — ${options.campaignTitle}`;
            logger_1.logger.warn('Using response verification template from filesystem (fallback)');
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
    async sendProfileLinkEmail(options) {
        const vars = {
            RECIPIENT_NAME: options.recipientName,
            PROFILE_URL: options.profileUrl,
            ORGANIZATION_NAME: options.organizationName,
            EXTRA_NOTES: options.extraNotes || '',
        };
        const dbTemplate = await this.loadTemplateFromDatabase('profile-link');
        let html, text, subject;
        if (dbTemplate) {
            html = await this.processTemplate(dbTemplate.html, vars);
            text = await this.processTextTemplate(dbTemplate.text, vars);
            subject = this.processSubject(dbTemplate.subject, vars);
        }
        else {
            const htmlTemplate = this.loadTemplate('profile-link', 'html');
            const txtTemplate = this.loadTemplate('profile-link', 'txt');
            html = await this.processTemplate(htmlTemplate, vars);
            text = await this.processTextTemplate(txtTemplate, vars);
            subject = `Your Profile — ${options.organizationName}`;
        }
        return this.sendEmail({
            to: options.recipientEmail,
            subject,
            html,
            text,
        });
    }
}
exports.emailService = new EmailService();
//# sourceMappingURL=email.service.js.map