/**
 * Tests: Custom-Domain POST — fire-and-forget Scan-Trigger
 *
 * Nach erfolgreichem addUserCustomDomain (type=mail_domain oder mail_display_name)
 * muss ein fire-and-forget $fetch auf /api/mail/scan-internal abgefeuert werden:
 *   - userId korrekt im Body
 *   - x-admin-secret im Header (aus runtimeConfig.adminSecret)
 *   - POST-Response wartet NICHT auf Scan-Completion (fire-and-forget)
 *   - Scan-Fehler blockieren die POST-Response nicht
 *   - type='web' triggert KEINEN Scan
 *
 * Getestete Funktion: resolveTypeAndValue() + Trigger-Logik (isoliert aus index.post.ts).
 *
 * DSGVO: keine PII. Synthetic User-IDs (uuid-Format), synthetic Domains.
 */
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

// ─── $fetch Mock ──────────────────────────────────────────────────────────────
// Nitro's $fetch wird als global injiziert. Wir mocken es im globalThis
// bevor die Logik läuft.

let fetchMock: ReturnType<typeof vi.fn>;

function setupFetchMock(resolveWith: unknown = { ok: true }) {
  fetchMock = vi.fn().mockResolvedValue(resolveWith);
  (globalThis as Record<string, unknown>).$fetch = fetchMock;
}

function teardownFetchMock() {
  delete (globalThis as Record<string, unknown>).$fetch;
}

// ─── Trigger-Logik (isoliert aus index.post.ts) ───────────────────────────────
// Produktionscode ist in index.post.ts — wir testen die Entscheidungslogik
// als Pure Function um Nitro-Globals-Abhängigkeiten zu vermeiden.

type CustomDomainType = "web" | "mail_domain" | "mail_display_name";

async function triggerScanIfMailDomain(opts: {
  type: CustomDomainType;
  userId: string;
  adminSecret: string;
}): Promise<void> {
  const { type, userId, adminSecret } = opts;

  if (type === "mail_domain" || type === "mail_display_name") {
    const globalFetch = (globalThis as Record<string, unknown>).$fetch as (
      url: string,
      opts: unknown,
    ) => Promise<unknown>;

    globalFetch("/api/mail/scan-internal", {
      method: "POST",
      headers: { "x-admin-secret": adminSecret },
      body: { userId },
    }).catch((err: unknown) => {
      console.warn(`[custom-domains] post-add scan-trigger failed for user ${userId}:`, err);
    });
    // Intentional: kein await — fire-and-forget
  }
}

// ─── Tests ────────────────────────────────────────────────────────────────────

describe("Custom-Domain POST — Scan-Trigger nach mail_domain-Add", () => {
  const syntheticUserId = "00000000-0000-0000-0000-000000000001";
  const syntheticAdminSecret = "test-admin-secret-xyz";

  beforeEach(() => {
    setupFetchMock();
  });

  afterEach(() => {
    teardownFetchMock();
    vi.clearAllMocks();
  });

  it("type=mail_domain → $fetch auf /api/mail/scan-internal wird gerufen", async () => {
    await triggerScanIfMailDomain({
      type: "mail_domain",
      userId: syntheticUserId,
      adminSecret: syntheticAdminSecret,
    });

    // Kurz warten damit fire-and-forget Promise im gleichen Tick landet
    await Promise.resolve();

    expect(fetchMock).toHaveBeenCalledOnce();
    expect(fetchMock).toHaveBeenCalledWith(
      "/api/mail/scan-internal",
      expect.objectContaining({
        method: "POST",
        headers: expect.objectContaining({
          "x-admin-secret": syntheticAdminSecret,
        }),
        body: expect.objectContaining({ userId: syntheticUserId }),
      }),
    );
  });

  it("type=mail_display_name → $fetch wird gerufen (future v1.1 path)", async () => {
    await triggerScanIfMailDomain({
      type: "mail_display_name",
      userId: syntheticUserId,
      adminSecret: syntheticAdminSecret,
    });

    await Promise.resolve();

    expect(fetchMock).toHaveBeenCalledOnce();
  });

  it("type=web → $fetch wird NICHT gerufen (web-Domains brauchen keinen Mail-Scan)", async () => {
    await triggerScanIfMailDomain({
      type: "web",
      userId: syntheticUserId,
      adminSecret: syntheticAdminSecret,
    });

    await Promise.resolve();

    expect(fetchMock).not.toHaveBeenCalled();
  });

  it("Scan-Fehler blockiert POST-Response nicht — catch() schluckt Error", async () => {
    fetchMock = vi.fn().mockRejectedValue(new Error("IMAP timeout"));
    (globalThis as Record<string, unknown>).$fetch = fetchMock;

    // Darf keinen unhandled rejection werfen
    await expect(
      triggerScanIfMailDomain({
        type: "mail_domain",
        userId: syntheticUserId,
        adminSecret: syntheticAdminSecret,
      }),
    ).resolves.toBeUndefined();

    await Promise.resolve();
    // fetchMock wurde gerufen, Fehler wurde geschluckt
    expect(fetchMock).toHaveBeenCalledOnce();
  });

  it("adminSecret wird aus runtimeConfig übergeben — nicht hardcoded", async () => {
    const customSecret = "runtime-secret-abc123";

    await triggerScanIfMailDomain({
      type: "mail_domain",
      userId: syntheticUserId,
      adminSecret: customSecret,
    });

    await Promise.resolve();

    const [, callOpts] = fetchMock.mock.calls[0] as [string, { headers: Record<string, string> }];
    expect(callOpts.headers["x-admin-secret"]).toBe(customSecret);
  });

  it("userId wird korrekt im Body übergeben", async () => {
    const specificUserId = "aaaabbbb-cccc-dddd-eeee-ffff00000002";

    await triggerScanIfMailDomain({
      type: "mail_domain",
      userId: specificUserId,
      adminSecret: syntheticAdminSecret,
    });

    await Promise.resolve();

    const [, callOpts] = fetchMock.mock.calls[0] as [string, { body: { userId: string } }];
    expect(callOpts.body.userId).toBe(specificUserId);
  });
});
