// Server-only — syncs newsletter opt-ins to Brevo's Contacts API via the
// double-opt-in endpoint: this only ever *requests* a subscription, it
// does not add the contact to the real list itself — Brevo sends the
// confirmation email (the template at BREVO_DOUBLE_OPTIN_TEMPLATE_ID,
// configured as this list's Double Opt-in template in Brevo's own UI) and
// only adds the contact to BREVO_LIST_ID once they click through. This
// app never sends marketing mail itself, and — as of this switch — never
// even directly grants list membership; it only ever hands Brevo the
// contact + consent-to-be-asked. Everything after that (the confirmation
// email itself, the post-confirmation Welcome Flow automation) is
// configured in Brevo's own UI, not manageable via their public API.
//
// Previously called the plain `POST /v3/contacts` upsert (single
// opt-in — added straight to the list, no confirmation click required).
// Switched 2026-07-25 per explicit request once the confirmation-email
// template existed to point templateId at.
const BREVO_DOUBLE_OPTIN_URL = "https://api.brevo.com/v3/contacts/doubleOptinConfirmation";
const BREVO_CONTACTS_URL = "https://api.brevo.com/v3/contacts";

export type BrevoSyncResult = { ok: true; alreadySubscribed?: boolean } | { ok: false; reason: string };

export type NewsletterOptInSource = "checkout" | "newsletter-page" | "newsletter-modal" | "newsletter-hero" | "challenge";

// Checked before calling doubleOptinConfirmation — that endpoint gives
// no way to tell "brand new signup" apart from "already confirmed,
// resending the same mail again" (verified directly: calling it a
// second time for an already-subscribed contact still returns a plain
// 201, same as the first time). `listIds` on a Brevo contact is only
// populated once double opt-in actually confirms (never for a merely
// *requested*, still-pending one), so its presence here is a reliable
// "already subscribed to this list" signal. Fails open on any error —
// this check is a UX nicety (skip an unnecessary resend, show a
// friendlier message), never a reason to block a real signup attempt.
async function isAlreadySubscribed(email: string, apiKey: string, listId: string): Promise<boolean> {
  try {
    const res = await fetch(`${BREVO_CONTACTS_URL}/${encodeURIComponent(email)}`, {
      headers: { "api-key": apiKey },
      signal: AbortSignal.timeout(5000),
    });
    if (!res.ok) return false; // 404 (never signed up before) or any transient error
    const contact: { listIds?: number[] } = await res.json();
    return (contact.listIds ?? []).includes(Number(listId));
  } catch {
    return false;
  }
}

// `source` becomes a Brevo contact attribute so campaigns/segments can
// tell a checkout opt-in apart from the standalone signup forms without
// needing separate lists.
export async function upsertNewsletterContact(
  email: string,
  source: NewsletterOptInSource,
): Promise<BrevoSyncResult> {
  const apiKey = process.env.BREVO_API_KEY;
  const listId = process.env.BREVO_LIST_ID;
  const templateId = process.env.BREVO_DOUBLE_OPTIN_TEMPLATE_ID;
  if (!apiKey || !listId || !templateId) {
    return { ok: false, reason: "BREVO_API_KEY/BREVO_LIST_ID/BREVO_DOUBLE_OPTIN_TEMPLATE_ID nicht konfiguriert." };
  }

  if (await isAlreadySubscribed(email, apiKey, listId)) {
    return { ok: true, alreadySubscribed: true };
  }

  const redirectionUrl = process.env.BREVO_DOI_REDIRECT_URL || "https://einfach-produktiv.mk360.de/newsletter-confirmed";

  try {
    const res = await fetch(BREVO_DOUBLE_OPTIN_URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "api-key": apiKey,
      },
      body: JSON.stringify({
        email,
        includeListIds: [Number(listId)],
        templateId: Number(templateId),
        redirectionUrl,
        attributes: { OPT_IN_SOURCE: source },
      }),
      signal: AbortSignal.timeout(8000),
    });
    // 201 Created is this endpoint's success status (unlike the plain
    // contacts upsert this replaced, which used 204). The already-
    // subscribed case is handled above, before this call ever fires.
    if (res.ok || res.status === 201) return { ok: true };
    const body = await res.json().catch(() => null);
    return { ok: false, reason: body?.message ?? `Brevo antwortete mit ${res.status}` };
  } catch (err) {
    return { ok: false, reason: err instanceof Error ? err.message : "Brevo ist gerade nicht erreichbar." };
  }
}
