import nodemailer from 'nodemailer';
import type { Transporter } from 'nodemailer';
import * as fs from 'fs';
import * as path from 'path';
import { env } from '../config/env';
import { logger } from '../utils/logger';
import { siteSettingsService } from '../modules/settings/settings.service';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

interface SendEmailOptions {
  to: string;
  subject: string;
  replyTo?: string;
  html: string;
  text: string;
}

interface SendEmailResult {
  success: boolean;
  messageId?: string;
  testMode: boolean;
}

interface SendCampaignEmailOptions {
  recipientEmail: string;
  recipientName?: string;
  recipientLevel?: string;
  userEmail: string;
  userName: string;
  postalCode: string;
  subject: string;
  message: string;
  campaignTitle: string;
}

// In-memory template cache (filesystem)
const templateCache = new Map<string, string>();

// Database template cache
interface DatabaseTemplate {
  html: string;
  text: string;
  subject: string;
}
const databaseTemplateCache = new Map<string, DatabaseTemplate>();

class EmailService {
  private transporter: Transporter;

  constructor() {
    this.transporter = this.createTransporter(env.SMTP_HOST, env.SMTP_PORT, env.SMTP_USER, env.SMTP_PASS);
  }

  private createTransporter(host: string, port: number, user: string, pass: string): Transporter {
    const config: Record<string, unknown> = { host, port };

    // Only set auth if user is provided (MailHog doesn't need auth)
    if (user) {
      config.auth = { user, pass };
    }

    return nodemailer.createTransport(config as nodemailer.TransportOptions);
  }

  /** Rebuild transporter from DB settings (with env fallback). Call after SMTP settings change. */
  async rebuildTransporter(): Promise<void> {
    try {
      const settings = await siteSettingsService.get();
      const provider = settings.smtpActiveProvider || 'mailhog';

      let host: string, port: number, user: string, pass: string;

      if (provider === 'mailhog') {
        host = env.SMTP_HOST;
        port = 1025;
        user = '';
        pass = '';
      } else {
        host = settings.smtpHost || env.SMTP_HOST;
        port = settings.smtpPort || env.SMTP_PORT;
        user = settings.smtpUser || env.SMTP_USER;
        pass = settings.smtpPass || env.SMTP_PASS;
      }

      this.transporter = this.createTransporter(host, port, user, pass);
      logger.info(`SMTP transporter rebuilt: provider=${provider} ${host}:${port} (auth=${!!user})`);
    } catch (err) {
      logger.error('Failed to rebuild SMTP transporter, keeping current config:', err);
    }
  }

  async testConnection(): Promise<boolean> {
    try {
      await this.transporter.verify();
      logger.info('SMTP connection verified');
      return true;
    } catch (err) {
      logger.error('SMTP connection failed:', err);
      return false;
    }
  }

  /**
   * Load template from database (with caching)
   */
  private async loadTemplateFromDatabase(key: string): Promise<DatabaseTemplate | null> {
    // 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: DatabaseTemplate = {
        html: template.htmlContent,
        text: template.textContent,
        subject: template.subjectLine,
      };

      // Cache for future use
      databaseTemplateCache.set(key, dbTemplate);

      logger.debug(`Loaded template "${key}" from database`);
      return dbTemplate;
    } catch (error) {
      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: string, type: 'html' | 'txt'): string {
    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.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?: string): void {
    if (key) {
      databaseTemplateCache.delete(key);
      logger.debug(`Cleared database cache for template: ${key}`);
    } else {
      databaseTemplateCache.clear();
      logger.debug('Cleared all database template cache');
    }
  }

  escapeHtml(unsafe: string): string {
    return unsafe
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#x27;');
  }

  /**
   * Fetch video metadata from Media API
   */
  private async getVideoMetadata(videoId: number): Promise<{
    id: number;
    title: string;
    thumbnailUrl: string;
    streamUrl: string;
    durationSeconds?: number;
    quality?: string;
    viewCount?: number;
  } | null> {
    try {
      const mediaApiUrl = 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() as {
        id: number;
        title: string;
        thumbnailUrl: string;
        streamUrl: string;
        durationSeconds?: number;
        quality?: string;
        viewCount?: number;
      };
    } catch (error) {
      logger.error(`Failed to fetch video ${videoId} metadata:`, error);
      return null;
    }
  }

  private formatDuration(seconds: number): string {
    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')}`;
  }

  private formatViewCount(count: number): string {
    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: string,
    vars: Record<string, string>,
    variableDefinitions?: Array<{ key: string; type: string; videoId?: number }>
  ): Promise<string> {
    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.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: string,
    vars: Record<string, string>,
    variableDefinitions?: Array<{ key: string; type: string; videoId?: number }>
  ): Promise<string> {
    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.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: string, vars: Record<string, string>): string {
    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;
  }

  private async getFromField(): Promise<string> {
    try {
      const settings = await siteSettingsService.get();
      const name = settings.emailFromName || env.SMTP_FROM_NAME;
      const address = settings.smtpFromAddress || env.SMTP_FROM;
      return `${name} <${address}>`;
    } catch {
      return `${env.SMTP_FROM_NAME} <${env.SMTP_FROM}>`;
    }
  }

  private async getOrganizationName(): Promise<string> {
    try {
      const settings = await siteSettingsService.get();
      return settings.organizationName || 'Changemaker Lite';
    } catch {
      return 'Changemaker Lite';
    }
  }

  private async getEmailTestConfig(): Promise<{ testMode: boolean; testRecipient: string }> {
    try {
      const settings = await siteSettingsService.get();
      return {
        testMode: settings.emailTestMode,
        testRecipient: settings.testEmailRecipient || env.TEST_EMAIL_RECIPIENT,
      };
    } catch {
      return {
        testMode: env.EMAIL_TEST_MODE === 'true',
        testRecipient: env.TEST_EMAIL_RECIPIENT,
      };
    }
  }

  async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {
    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.info(`Email sent: ${info.messageId} to=${to} testMode=${testMode}`);

      return {
        success: true,
        messageId: info.messageId,
        testMode,
      };
    } catch (err) {
      logger.error('Failed to send email:', err);
      return {
        success: false,
        testMode,
      };
    }
  }

  async sendCampaignEmail(options: SendCampaignEmailOptions): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;

    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.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.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(): Promise<boolean> {
    try {
      const settings = await siteSettingsService.get();
      return settings.smtpActiveProvider === 'production' && !!settings.smtpHost;
    } catch {
      // env.SMTP_HOST defaults to 'mailhog-changemaker' — if it's still the default, SMTP isn't configured
      const defaultHost = 'mailhog';
      return !env.SMTP_HOST.startsWith(defaultHost) && !!env.SMTP_HOST;
    }
  }

  async sendVerificationEmail(options: {
    recipientEmail: string;
    recipientName: string;
    verificationUrl: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      RECIPIENT_NAME: options.recipientName || 'there',
      VERIFICATION_URL: options.verificationUrl,
      ORGANIZATION_NAME: orgName,
      EXPIRY_HOURS: '24',
    };

    const dbTemplate = await this.loadTemplateFromDatabase('email-verification');

    let html: string, text: string, subject: string;
    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: {
    recipientEmail: string;
    recipientName: string;
    resetUrl: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      RECIPIENT_NAME: options.recipientName || 'there',
      RESET_URL: options.resetUrl,
      ORGANIZATION_NAME: orgName,
    };

    const dbTemplate = await this.loadTemplateFromDatabase('password-reset');

    let html: string, text: string, subject: string;
    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: {
    recipientEmail: string;
    recipientName: string;
    loginUrl: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      RECIPIENT_NAME: options.recipientName || 'there',
      LOGIN_URL: options.loginUrl,
      ORGANIZATION_NAME: orgName,
    };

    const dbTemplate = await this.loadTemplateFromDatabase('account-approved');

    let html: string, text: string, subject: string;
    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: {
    adminEmails: string[];
    newUserEmail: string;
    newUserName: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const adminUrl = env.ADMIN_URL || 'http://localhost:3000';
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    recipientEmail: string;
    recipientName: string;
    shiftTitle: string;
    shiftDate: string;
    shiftTime: string;
    shiftLocation: string;
    isNewUser: boolean;
    tempPassword?: string;
    loginUrl: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    recipientEmail: string;
    recipientName: string;
    shiftTitle: string;
    shiftDate: string;
    shiftStartTime: string;
    shiftEndTime: string;
    shiftLocation: string;
    shiftDescription: string;
    currentVolunteers: number;
    maxVolunteers: number;
    shiftStatus: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    adminEmails: string[];
    shiftTitle: string;
    shiftDate: string;
    volunteerName: string;
    volunteerEmail: string;
    signupSource: string;
    adminUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    adminEmails: string[];
    shiftTitle: string;
    shiftDate: string;
    volunteerName: string;
    volunteerEmail: string;
    cancellationSource: string;
    adminUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    adminEmails: string[];
    campaignTitle: string;
    representativeName: string;
    responseType: string;
    submitterName: string;
    adminUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    adminEmails: string[];
    volunteerName: string;
    address: string;
    shiftTitle: string;
    signSize: string;
    adminUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    volunteerEmail: string;
    volunteerName: string;
    cutName: string;
    sessionDate: string;
    visitCount: number;
    durationMinutes: number;
    distanceKm: number;
    outcomeBreakdown: Record<string, number>;
  }): Promise<void> {
    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: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    volunteerEmail: string;
    volunteerName: string;
    shiftTitle: string;
    shiftDate: string;
    shiftTime: string;
    signupUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    volunteerEmail: string;
    volunteerName: string;
    shiftTitle: string;
    shiftDate: string;
    shiftTime: string;
    shiftLocation: string;
    signupUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    volunteerEmail: string;
    volunteerName: string;
    lastActivityDate: string;
    lastActivityType: string;
    signupUrl: string;
  }): Promise<void> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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: {
    recipientEmail: string;
    campaignTitle: string;
    responseType: string;
    responseText: string;
    submitterName: string;
    verificationUrl: string;
    reportUrl: string;
  }): Promise<SendEmailResult> {
    const orgName = await this.getOrganizationName();
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;

    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.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.warn('Using response verification template from filesystem (fallback)');
    }

    return this.sendEmail({
      to: options.recipientEmail,
      subject,
      html,
      text,
    });
  }
  async sendProfileLinkEmail(options: {
    recipientEmail: string;
    recipientName: string;
    profileUrl: string;
    organizationName: string;
    extraNotes?: string;
  }): Promise<SendEmailResult> {
    const vars: Record<string, string> = {
      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: string, text: string, subject: string;
    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,
    });
  }
}

export const emailService = new EmailService();
