import { Injectable, Logger, ServiceUnavailableException } from '@nestjs/common';

/**
 * MailerService — thin wrapper over Brevo's transactional email API.
 *
 * Why Brevo: free tier (300 emails/day) covers dev/staging volume,
 * REST API means no SMTP credentials to negotiate per-tenant, and
 * the integration is a single API key in env. Per stakeholder
 * decision Q1=A: one tenant-wide Brevo identity for the dev stage;
 * per-tenant SMTP is a Phase 2 white-label concern.
 *
 * Why fetch (no SDK): Brevo's official `@getbrevo/brevo` SDK is a
 * heavy package; one outbound POST is trivial against the bare API
 * with Node 18+'s native fetch. Keeps the dep tree clean.
 *
 * Failure semantics: `send()` throws ServiceUnavailableException
 * when the API key is missing or the API call fails. Callers in
 * security-critical paths (forgot-password) catch this and fall
 * back to a generic success response so the API doesn't leak
 * "this email exists, but our mailer broke."
 *
 * Env config (rules/security.md A02 — never commit secrets):
 *   BREVO_API_KEY     — long-lived API key (xkeysib-...)
 *   BREVO_FROM_EMAIL  — verified sender, e.g. noreply@dmcops.com
 *   BREVO_FROM_NAME   — display name, e.g. "DMC Ops"
 *   BREVO_REPLY_TO    — optional reply-to (defaults to FROM_EMAIL)
 */

export interface MailRecipient {
  email: string;
  name?: string;
}

export interface SendMailInput {
  /** One address or several — error alerts fan out to the dev list. */
  to: MailRecipient | MailRecipient[];
  subject: string;
  html: string;
  /** Plain-text fallback; auto-derived from html if omitted. */
  text?: string;
  /** Sender display name (e.g. tenant company name); defaults to BREVO_FROM_NAME. */
  fromName?: string;
}

@Injectable()
export class MailerService {
  private readonly logger = new Logger(MailerService.name);

  private get apiKey(): string | null {
    return process.env.BREVO_API_KEY?.trim() || null;
  }
  private get fromEmail(): string {
    // Default points at workzy.co — the domain already used for the
    // dev server (dmc.workzy.co), so no fresh DNS verification at
    // Brevo for the dev-stage rollout. Override via env per env.
    return process.env.BREVO_FROM_EMAIL?.trim() || 'noreply@workzy.co';
  }
  private get fromName(): string {
    return process.env.BREVO_FROM_NAME?.trim() || 'DMC Ops';
  }
  private get replyTo(): string {
    return process.env.BREVO_REPLY_TO?.trim() || this.fromEmail;
  }

  /**
   * True only when the env is configured. Useful at startup to log
   * a warning when ops has forgotten to set BREVO_API_KEY.
   */
  isConfigured(): boolean {
    return !!this.apiKey;
  }

  async send(input: SendMailInput): Promise<{ messageId: string | null }> {
    if (!this.apiKey) {
      // Fail loudly in logs but return a typed exception so the
      // caller can decide how to handle (forgot-password swallows
      // it; admin-driven flows propagate as 503).
      this.logger.error('BREVO_API_KEY not set — mail send refused');
      throw new ServiceUnavailableException('Mailer is not configured');
    }
    const recipients = (Array.isArray(input.to) ? input.to : [input.to]).map((r) => ({
      email: r.email,
      ...(r.name ? { name: r.name } : {}),
    }));
    const body = {
      sender: { email: this.fromEmail, name: input.fromName?.trim() || this.fromName },
      to: recipients,
      replyTo: { email: this.replyTo },
      subject: input.subject,
      htmlContent: input.html,
      ...(input.text ? { textContent: input.text } : {}),
    };
    try {
      const r = await fetch('https://api.brevo.com/v3/smtp/email', {
        method: 'POST',
        headers: {
          'api-key': this.apiKey,
          'content-type': 'application/json',
          accept: 'application/json',
        },
        body: JSON.stringify(body),
      });
      if (!r.ok) {
        const errText = await r.text().catch(() => '');
        this.logger.error(`Brevo send failed: ${r.status} ${errText.slice(0, 200)}`);
        throw new ServiceUnavailableException('Mail send failed');
      }
      const json = (await r.json().catch(() => ({}))) as { messageId?: string };
      return { messageId: json?.messageId ?? null };
    } catch (err) {
      if (err instanceof ServiceUnavailableException) throw err;
      this.logger.error(`Brevo send threw: ${(err as Error).message}`);
      throw new ServiceUnavailableException('Mail send failed');
    }
  }
}
