import { Injectable, Logger, ServiceUnavailableException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NotificationChannel } from '@prisma/client';
import nodemailer, { type Transporter } from 'nodemailer';

/**
 * Unified notification provider interface.
 * Real implementations (Twilio, SendGrid, WhatsApp Business API) can be
 * swapped in per-channel without touching calling code.
 */
export interface NotificationProvider {
  readonly channel: NotificationChannel;
  send(params: {
    recipient: string;
    subject?: string;
    body: string;
    metadata?: Record<string, unknown>;
  }): Promise<{ providerMessageId: string; accepted: boolean; error?: string }>;
}

/**
 * Production email provider backed by SMTP.
 * In production, missing SMTP settings fail closed instead of pretending that
 * messages were delivered.
 */
@Injectable()
export class SmtpEmailProvider implements NotificationProvider {
  readonly channel = NotificationChannel.EMAIL;
  private readonly logger = new Logger('EmailProvider');
  private transporter: Transporter | null = null;

  constructor(private readonly config: ConfigService) {}

  async send({ recipient, subject, body }: {
    recipient: string;
    subject?: string;
    body: string;
  }) {
    const fromEmail = this.config.get<string>('SMTP_FROM_EMAIL');
    const fromName = this.config.get<string>('SMTP_FROM_NAME', 'SyndiClub');
    const replyTo = this.config.get<string>('SMTP_REPLY_TO');

    if (!this.isConfigured() || !fromEmail) {
      if (this.config.get<string>('ENABLE_STUB_PROVIDERS') === 'true') {
        this.logger.warn(
          `SMTP not configured. Dev-only email accepted for ${recipient}: ${subject ?? '(sans sujet)'}`,
        );
        return { providerMessageId: `dev-email-${Date.now()}`, accepted: true };
      }
      throw new ServiceUnavailableException('SMTP email provider is not configured');
    }

    const info = await this.getTransporter().sendMail({
      from: `"${fromName}" <${fromEmail}>`,
      to: recipient,
      replyTo,
      subject,
      text: body,
    });

    return {
      providerMessageId: String(info.messageId),
      accepted: Array.isArray(info.accepted) ? info.accepted.length > 0 : true,
    };
  }

  private isConfigured() {
    return Boolean(
      this.config.get<string>('SMTP_HOST') &&
        this.config.get<string>('SMTP_PORT') &&
        this.config.get<string>('SMTP_USER') &&
        this.config.get<string>('SMTP_PASSWORD'),
    );
  }

  private getTransporter() {
    if (!this.transporter) {
      this.transporter = nodemailer.createTransport({
        host: this.config.getOrThrow<string>('SMTP_HOST'),
        port: Number(this.config.get<string>('SMTP_PORT', '587')),
        secure: this.config.get<string>('SMTP_SECURE', 'false') === 'true',
        auth: {
          user: this.config.getOrThrow<string>('SMTP_USER'),
          pass: this.config.getOrThrow<string>('SMTP_PASSWORD'),
        },
      });
    }
    return this.transporter;
  }
}

@Injectable()
export class InAppProvider implements NotificationProvider {
  readonly channel = NotificationChannel.IN_APP;
  async send() {
    // In-app notifications are stored in DB only; no external send.
    return { providerMessageId: `inapp-${Date.now()}`, accepted: true };
  }
}
