// Mailer 抽象 + 実装（DryRun / Resend / Failing[テスト用]）
import type { OutMessage, SendResult } from "./types.js";

export interface Mailer {
  /**
   * 1通送信。idempotencyKey で Resend 側の二重送信も防止。
   * 戻り値 ok=false は配送失敗（呼び出し側で failed 記録 → 次ティック再開）。
   */
  send(msg: OutMessage, from: string, idempotencyKey: string): Promise<SendResult>;
  /** 実送信を行うか（DRY_RUN の表示用） */
  readonly live: boolean;
}

/** DRY_RUN: 実際には送らず成功扱い（id=dry:...） */
export class DryRunMailer implements Mailer {
  readonly live = false;
  sent: { msg: OutMessage; key: string }[] = [];
  async send(msg: OutMessage, _from: string, key: string): Promise<SendResult> {
    this.sent.push({ msg, key });
    return { ok: true, id: `dry:${key}` };
  }
}

/** 本番: Resend HTTP API */
export class ResendMailer implements Mailer {
  readonly live = true;
  constructor(private apiKey: string) {}
  async send(msg: OutMessage, from: string, key: string): Promise<SendResult> {
    try {
      const res = await fetch("https://api.resend.com/emails", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${this.apiKey}`,
          "Content-Type": "application/json",
          "Idempotency-Key": key,
        },
        body: JSON.stringify({
          from,
          to: msg.to,
          subject: msg.subject,
          html: msg.html,
        }),
      });
      if (!res.ok) {
        const text = await res.text();
        return { ok: false, error: `resend ${res.status}: ${text.slice(0, 200)}` };
      }
      const data = (await res.json()) as { id?: string };
      return { ok: true, id: data.id };
    } catch (e) {
      return { ok: false, error: String(e) };
    }
  }
}

/** テスト用: 指定回数だけ成功し、その後失敗（外部障害シミュレーション） */
export class ScriptedMailer implements Mailer {
  readonly live = true;
  sent: { msg: OutMessage; key: string }[] = [];
  private calls = 0;
  constructor(private failOnCall: number[] = []) {}
  async send(msg: OutMessage, _from: string, key: string): Promise<SendResult> {
    this.calls++;
    if (this.failOnCall.includes(this.calls))
      return { ok: false, error: "simulated failure" };
    this.sent.push({ msg, key });
    return { ok: true, id: `live:${key}` };
  }
}
