// ==================== SMTP SERVICE ====================
// Service für E-Mail-Versand via SMTP

import nodemailer from 'nodemailer';
import type { Transporter } from 'nodemailer';
import MailComposer from 'nodemailer/lib/mail-composer';

// Verschlüsselungstyp
export type MailEncryption = 'SSL' | 'STARTTLS' | 'NONE';

export interface SmtpCredentials {
  host: string;
  port: number;
  user: string;
  password: string;
  encryption?: MailEncryption;     // SSL, STARTTLS oder NONE (Standard: SSL)
  allowSelfSignedCerts?: boolean;  // Selbstsignierte Zertifikate erlauben
  // DNS-Rebinding-Schutz: Caller hat den Hostname zu IP aufgelöst und
  // setzt host=IP, servername=originalHostname für TLS-SNI / Cert-Validation.
  // Damit kann ein zweiter DNS-Lookup nicht plötzlich auf eine interne IP zeigen.
  servername?: string;
}

// Anhang-Interface
export interface EmailAttachment {
  filename: string;
  content: string;       // Base64-kodierter Inhalt
  contentType?: string;  // MIME-Type (z.B. 'application/pdf')
}

export interface SendEmailParams {
  to: string | string[];
  cc?: string | string[];
  subject: string;
  text?: string;
  html?: string;
  inReplyTo?: string;     // Message-ID der E-Mail auf die geantwortet wird
  references?: string[];  // Thread-Referenzen
  attachments?: EmailAttachment[];  // Anhänge
}

export interface SendEmailResult {
  success: boolean;
  messageId?: string;
  rawEmail?: Buffer;    // RFC 5322 formatierte E-Mail für IMAP-Speicherung
  error?: string;
}

// Optionaler Logging-Kontext
export interface EmailLogContext {
  context: string;         // z.B. "consent-link", "authorization-request", "customer-email"
  customerId?: number;
  triggeredBy?: string;    // User-Email
}

// Security: zentrale CRLF-Prüfung gegen SMTP-Header-Injection.
// Alle Felder, die als Header ausgehen (to/cc/subject/replyTo/references/from),
// werden hier geprüft – egal ob der Caller aus cachedEmail, birthday, gdpr,
// consent-public oder auth kommt.
function containsCRLF(value: unknown): boolean {
  if (typeof value === 'string') return /[\r\n]/.test(value);
  if (Array.isArray(value)) return value.some(containsCRLF);
  return false;
}

// E-Mail senden
export async function sendEmail(
  credentials: SmtpCredentials,
  fromAddress: string,
  params: SendEmailParams,
  logContext?: EmailLogContext
): Promise<SendEmailResult> {
  // Header-Injection-Guard (defensiv: Absender, Empfänger, Subject)
  if (
    containsCRLF(fromAddress) ||
    containsCRLF(params.to) ||
    containsCRLF(params.cc) ||
    containsCRLF(params.subject) ||
    containsCRLF(params.inReplyTo) ||
    containsCRLF(params.references)
  ) {
    return {
      success: false,
      error: 'Ungültige Zeichen in E-Mail-Header-Feldern (CRLF nicht erlaubt)',
    };
  }

  // Verschlüsselungs-Einstellungen basierend auf Modus
  const encryption = credentials.encryption ?? 'SSL';
  const rejectUnauthorized = !credentials.allowSelfSignedCerts;

  // Transport-Optionen je nach Verschlüsselungstyp
  // SSL: secure=true (implicit TLS, Port 465)
  // STARTTLS: secure=false (upgrades to TLS, Port 587)
  // NONE: secure=false + ignoreTLS=true (no encryption, Port 25)
  const transportOptions: nodemailer.TransportOptions & {
    host: string;
    port: number;
    secure: boolean;
    auth: { user: string; pass: string };
    tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string; servername?: string };
    ignoreTLS?: boolean;
    requireTLS?: boolean;
    connectionTimeout: number;
    greetingTimeout: number;
    socketTimeout: number;
  } = {
    host: credentials.host,
    port: credentials.port,
    secure: encryption === 'SSL',
    auth: {
      user: credentials.user,
      pass: credentials.password,
    },
    connectionTimeout: 10000,
    greetingTimeout: 10000,
    socketTimeout: 30000,
  };

  // TLS-Optionen nur wenn nicht NONE
  if (encryption !== 'NONE') {
    transportOptions.tls = { rejectUnauthorized };
    // DNS-Rebinding-Schutz: wenn host eine IP ist, der ursprüngliche
    // Hostname für SNI/Cert-Validation explizit setzen.
    if (credentials.servername) {
      transportOptions.tls.servername = credentials.servername;
    }
    if (credentials.allowSelfSignedCerts) {
      // Auch ältere TLS-Versionen + legacy Cipher-Suites für alte Server zulassen
      transportOptions.tls.minVersion = 'TLSv1';
      transportOptions.tls.ciphers = 'DEFAULT:@SECLEVEL=0';
    }
  } else {
    // Keine Verschlüsselung: STARTTLS ignorieren
    transportOptions.ignoreTLS = true;
  }

  // Bei STARTTLS: requireTLS erzwingen
  if (encryption === 'STARTTLS') {
    transportOptions.requireTLS = true;
  }

  // Debug-Logging für Entwicklung
  console.log(`[SMTP] Connecting to ${credentials.host}:${credentials.port} (${encryption}), user: ${credentials.user}`);

  // Transporter erstellen
  const transporter: Transporter = nodemailer.createTransport(transportOptions);

  try {
    // E-Mail-Optionen zusammenstellen
    const mailOptions: nodemailer.SendMailOptions = {
      from: fromAddress,
      to: Array.isArray(params.to) ? params.to.join(', ') : params.to,
      subject: params.subject,
    };

    // CC hinzufügen falls vorhanden
    if (params.cc) {
      mailOptions.cc = Array.isArray(params.cc) ? params.cc.join(', ') : params.cc;
    }

    // Body hinzufügen
    if (params.html) {
      mailOptions.html = params.html;
      // Auch Text-Version für Clients ohne HTML-Support
      mailOptions.text = params.text || stripHtml(params.html);
    } else if (params.text) {
      mailOptions.text = params.text;
    }

    // Threading-Header für Antworten
    if (params.inReplyTo) {
      mailOptions.inReplyTo = params.inReplyTo;
    }
    if (params.references && params.references.length > 0) {
      mailOptions.references = params.references.join(' ');
    }

    // Anhänge hinzufügen
    if (params.attachments && params.attachments.length > 0) {
      mailOptions.attachments = params.attachments.map((att) => ({
        filename: att.filename,
        content: Buffer.from(att.content, 'base64'),
        contentType: att.contentType,
      }));
    }

    // E-Mail senden
    const result = await transporter.sendMail(mailOptions);

    // Raw E-Mail für IMAP-Speicherung bauen (mit tatsächlicher Message-ID)
    let rawEmail: Buffer | undefined;
    try {
      const composerOptions = {
        ...mailOptions,
        messageId: result.messageId, // Tatsächliche Message-ID vom Server
      };
      const composer = new MailComposer(composerOptions);
      rawEmail = await composer.compile().build();
    } catch (compileError) {
      console.error('Error compiling raw email:', compileError);
      // Nicht kritisch - E-Mail wurde trotzdem gesendet
    }

    // E-Mail-Log erstellen (async, nicht blockierend)
    if (logContext) {
      import('./emailLog.service.js').then(({ createEmailLog }) => {
        createEmailLog({
          fromAddress,
          toAddress: Array.isArray(params.to) ? params.to.join(', ') : params.to,
          subject: params.subject,
          context: logContext.context,
          customerId: logContext.customerId,
          triggeredBy: logContext.triggeredBy,
          smtpServer: credentials.host,
          smtpPort: credentials.port,
          smtpEncryption: credentials.encryption ?? 'SSL',
          smtpUser: credentials.user,
          success: true,
          messageId: result.messageId,
          smtpResponse: result.response,
        }).catch((err) => console.error('EmailLog write error:', err));
      });
    }

    return {
      success: true,
      messageId: result.messageId,
      rawEmail,
    };
  } catch (error) {
    console.error('SMTP sendEmail error:', error);

    // Bessere Fehlermeldungen
    let errorMessage = 'Unbekannter Fehler beim E-Mail-Versand';

    if (error instanceof Error) {
      const msg = error.message.toLowerCase();
      const errorCode = (error as NodeJS.ErrnoException).code?.toLowerCase() || '';

      if (msg.includes('authentication') || msg.includes('auth') || msg.includes('login')) {
        errorMessage = 'SMTP-Authentifizierung fehlgeschlagen - Zugangsdaten prüfen';
      } else if (msg.includes('econnrefused') || errorCode === 'econnrefused') {
        errorMessage = `SMTP-Server nicht erreichbar: ${credentials.host}:${credentials.port} - Verbindung verweigert`;
      } else if (msg.includes('greeting never received') || msg.includes('etimedout') || errorCode === 'etimedout') {
        // Detaillierte Fehlermeldung je nach Port/Verschlüsselung
        const enc = credentials.encryption ?? 'SSL';
        if (enc === 'STARTTLS' && credentials.port === 587) {
          errorMessage = `SMTP-Verbindung zu Port 587 fehlgeschlagen - STARTTLS (Submission) ist möglicherweise nicht aktiviert auf ${credentials.host}`;
        } else if (enc === 'NONE' && credentials.port === 25) {
          errorMessage = `SMTP-Verbindung zu Port 25 fehlgeschlagen - Port möglicherweise blockiert oder nicht erreichbar auf ${credentials.host}`;
        } else {
          errorMessage = `SMTP-Verbindung zu ${credentials.host}:${credentials.port} fehlgeschlagen - Port nicht erreichbar oder Timeout`;
        }
      } else if (msg.includes('timeout')) {
        errorMessage = `SMTP-Verbindung: Zeitüberschreitung bei ${credentials.host}:${credentials.port}`;
      } else if (msg.includes('recipient') || msg.includes('rejected')) {
        errorMessage = 'Empfänger-Adresse wurde vom Server abgelehnt';
      } else if (msg.includes('certificate') || msg.includes('cert')) {
        errorMessage = 'SSL-Zertifikatfehler - Aktiviere "Selbstsignierte Zertifikate erlauben" in den Provider-Einstellungen';
      } else if (msg.includes('socket close') || msg.includes('socket hang up') || msg.includes('econnreset') || errorCode === 'econnreset') {
        // Server schließt Verbindung unerwartet - oft TLS-Problem bei STARTTLS
        const enc = credentials.encryption ?? 'SSL';
        if (enc === 'STARTTLS') {
          errorMessage = `SMTP-Verbindung abgebrochen bei STARTTLS - Aktiviere "Selbstsignierte Zertifikate erlauben" oder verwende SSL/TLS auf Port 465`;
        } else {
          errorMessage = `SMTP-Verbindung unerwartet geschlossen von ${credentials.host}:${credentials.port}`;
        }
      } else {
        errorMessage = error.message;
      }
    }

    // E-Mail-Log erstellen (Fehler)
    if (logContext) {
      import('./emailLog.service.js').then(({ createEmailLog }) => {
        createEmailLog({
          fromAddress,
          toAddress: Array.isArray(params.to) ? params.to.join(', ') : params.to,
          subject: params.subject,
          context: logContext.context,
          customerId: logContext.customerId,
          triggeredBy: logContext.triggeredBy,
          smtpServer: credentials.host,
          smtpPort: credentials.port,
          smtpEncryption: credentials.encryption ?? 'SSL',
          smtpUser: credentials.user,
          success: false,
          errorMessage,
        }).catch((err) => console.error('EmailLog write error:', err));
      });
    }

    return {
      success: false,
      error: errorMessage,
    };
  } finally {
    // Transporter schließen
    transporter.close();
  }
}

// SMTP-Verbindung testen
export async function testSmtpConnection(credentials: SmtpCredentials): Promise<void> {
  // Verschlüsselungs-Einstellungen basierend auf Modus
  const encryption = credentials.encryption ?? 'SSL';
  const rejectUnauthorized = !credentials.allowSelfSignedCerts;

  const transportOptions: nodemailer.TransportOptions & {
    host: string;
    port: number;
    secure: boolean;
    auth: { user: string; pass: string };
    tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string; servername?: string };
    ignoreTLS?: boolean;
    connectionTimeout: number;
    greetingTimeout: number;
  } = {
    host: credentials.host,
    port: credentials.port,
    secure: encryption === 'SSL',
    auth: {
      user: credentials.user,
      pass: credentials.password,
    },
    connectionTimeout: 10000,
    greetingTimeout: 10000,
  };

  if (encryption !== 'NONE') {
    transportOptions.tls = { rejectUnauthorized };
    if (credentials.servername) {
      transportOptions.tls.servername = credentials.servername;
    }
  } else {
    transportOptions.ignoreTLS = true;
  }

  const transporter: Transporter = nodemailer.createTransport(transportOptions);

  try {
    // Verbindung verifizieren
    await transporter.verify();
  } catch (error) {
    if (error instanceof Error) {
      const msg = error.message.toLowerCase();
      const errorCode = (error as NodeJS.ErrnoException).code?.toLowerCase() || '';

      if (msg.includes('authentication') || msg.includes('auth') || msg.includes('login')) {
        throw new Error('SMTP-Authentifizierung fehlgeschlagen');
      }
      if (msg.includes('econnrefused') || errorCode === 'econnrefused') {
        throw new Error(`SMTP-Server nicht erreichbar: ${credentials.host}:${credentials.port} - Verbindung verweigert`);
      }
      if (msg.includes('greeting never received') || msg.includes('etimedout') || errorCode === 'etimedout') {
        if (encryption === 'STARTTLS' && credentials.port === 587) {
          throw new Error(`SMTP Port 587 (STARTTLS/Submission) ist nicht erreichbar - In Plesk unter Tools & Settings > Mail Server Settings aktivieren`);
        } else if (encryption === 'NONE' && credentials.port === 25) {
          throw new Error(`SMTP Port 25 ist nicht erreichbar auf ${credentials.host}`);
        } else {
          throw new Error(`SMTP-Verbindung zu ${credentials.host}:${credentials.port} fehlgeschlagen - Port nicht erreichbar`);
        }
      }
      if (msg.includes('certificate') || msg.includes('cert')) {
        throw new Error('SSL-Zertifikatfehler - Aktiviere "Selbstsignierte Zertifikate erlauben"');
      }
    }

    throw error;
  } finally {
    transporter.close();
  }
}

// Helper: HTML zu Text konvertieren (einfache Version)
function stripHtml(html: string): string {
  return html
    // Zeilenumbrüche für Block-Elemente
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/p>/gi, '\n\n')
    .replace(/<\/div>/gi, '\n')
    .replace(/<\/li>/gi, '\n')
    // Alle HTML-Tags entfernen
    .replace(/<[^>]+>/g, '')
    // HTML-Entities dekodieren
    .replace(/&nbsp;/g, ' ')
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    // Mehrfache Leerzeilen reduzieren
    .replace(/\n{3,}/g, '\n\n')
    .trim();
}
