import 'server-only';

import type SMTPTransport from 'nodemailer/lib/smtp-transport';
import type { Transporter } from 'nodemailer';
import type {
  EmailProvider,
  EmailProviderConfig,
  EmailTestResult,
  SendEmailInput,
  SendEmailRecipient,
  SendEmailResult,
} from './email-provider';

function formatRecipient(recipient: SendEmailRecipient): string {
  return recipient.name?.trim()
    ? `"${recipient.name.trim()}" <${recipient.email}>`
    : recipient.email;
}

function getFromValue(config: EmailProviderConfig): string {
  return config.fromName?.trim()
    ? `"${config.fromName.trim()}" <${config.fromEmail}>`
    : config.fromEmail;
}

async function createTransporter(config: EmailProviderConfig): Promise<Transporter> {
  const nodemailer = await import('nodemailer');

  return nodemailer.createTransport({
    host: config.host,
    port: config.port,
    secure: config.secure,
    auth: config.username
      ? {
          user: config.username,
          pass: config.password ?? undefined,
        }
      : undefined,
  } satisfies SMTPTransport.Options);
}

function normalizeRecipients(values: Array<string | { address: string }> | undefined): string[] {
  return Array.isArray(values) ? values.map((value) => String(value)) : [];
}

export class SmtpEmailProvider implements EmailProvider {
  async testConnection(config: EmailProviderConfig): Promise<EmailTestResult> {
    const transporter = await createTransporter(config);

    try {
      await transporter.verify();
      return { ok: true };
    } catch (error) {
      return {
        ok: false,
        detail: error instanceof Error ? error.message : 'Unknown SMTP health check failure',
      };
    }
  }

  async sendEmail(config: EmailProviderConfig, input: SendEmailInput): Promise<SendEmailResult> {
    const transporter = await createTransporter(config);
    const info = await transporter.sendMail({
      from: getFromValue(config),
      to: input.to.map(formatRecipient),
      cc: input.cc?.map(formatRecipient),
      bcc: input.bcc?.map(formatRecipient),
      replyTo: input.replyTo ?? config.replyToEmail ?? undefined,
      subject: input.subject,
      text: input.text,
      html: input.html ?? undefined,
      attachments: input.attachments?.map((attachment) => ({
        filename: attachment.fileName,
        content: attachment.buffer,
        contentType: attachment.contentType,
      })),
    });

    return {
      providerMessageId: info.messageId ?? null,
      acceptedRecipients: normalizeRecipients(info.accepted),
      rejectedRecipients: normalizeRecipients(info.rejected),
      metadata: {
        envelope: info.envelope,
        response: info.response ?? null,
      },
    };
  }
}
