import nodemailer, { type Transporter } from 'nodemailer';
import type { PasswordResetMailer } from '../domain/ports.js';

export interface SmtpPasswordResetMailerOptions {
  host: string;
  port: number;
  secure: boolean;
  user: string;
  password: string;
  from: string;
}

/** Sends password-reset messages through an authenticated SMTP server. */
export class SmtpPasswordResetMailer implements PasswordResetMailer {
  private readonly transporter: Transporter;

  constructor(private readonly options: SmtpPasswordResetMailerOptions) {
    this.transporter = nodemailer.createTransport({
      host: options.host,
      port: options.port,
      secure: options.secure,
      auth: { user: options.user, pass: options.password },
    });
  }

  isConfigured(): boolean {
    return true;
  }

  async assertReady(): Promise<void> {
    return;
  }

  async sendPasswordReset(input: { email: string; resetUrl: string; locale?: string }): Promise<void> {
    await this.transporter.sendMail({
      from: this.options.from,
      to: input.email,
      subject: 'Restablece tu contraseña — Mercado de Vida',
      text: [
        'Has solicitado restablecer tu contraseña de Mercado de Vida.',
        '',
        `Abre este enlace para continuar: ${input.resetUrl}`,
        '',
        'Si no solicitaste este cambio, puedes ignorar este correo.',
        'El enlace caduca en una hora y solo puede utilizarse una vez.',
      ].join('\n'),
      html: [
        '<p>Has solicitado restablecer tu contraseña de Mercado de Vida.</p>',
        `<p><a href="${escapeHtml(input.resetUrl)}">Restablecer contraseña</a></p>`,
        '<p>Si no solicitaste este cambio, puedes ignorar este correo.</p>',
        '<p>El enlace caduca en una hora y solo puede utilizarse una vez.</p>',
      ].join(''),
    });
  }
}

export function createPasswordResetMailer(env: NodeJS.ProcessEnv = process.env): PasswordResetMailer {
  const host = env.SMTP_HOST?.trim();
  const user = env.SMTP_USER?.trim();
  const password = env.SMTP_PASS;
  const from = env.SMTP_FROM?.trim() || user;
  if (!host || !user || !password || !from) {
    return {
      isConfigured: () => false,
      async assertReady() {
        throw new Error('SMTP is not configured; set SMTP_HOST, SMTP_USER, SMTP_PASS and SMTP_FROM');
      },
      async sendPasswordReset() {
        throw new Error('SMTP is not configured; set SMTP_HOST, SMTP_USER, SMTP_PASS and SMTP_FROM');
      },
    };
  }

  const port = Number(env.SMTP_PORT ?? '587');
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
    throw new Error('SMTP_PORT must be a valid TCP port');
  }
  const secure = env.SMTP_SECURE === 'true' || port === 465;
  return new SmtpPasswordResetMailer({ host, port, secure, user, password, from });
}

function escapeHtml(value: string): string {
  return value.replace(/[&<>"']/g, (character) => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
  })[character] ?? character);
}
