type Env = Record<string, string>;

type SignupBody = {
  firstName?: string;
  lastName?: string;
  email?: string;
  newsletter?: string;
  source?: string;
  consentTextVersion?: string;
};

const CORS_ALLOWLIST = new Set([
  'https://cultscale.com',
  'https://www.cultscale.com',
  'https://cultshot.com',
  'https://www.cultshot.com',
  'http://localhost:4321',
  'http://localhost:4322',
  'http://127.0.0.1:4321',
  'http://127.0.0.1:4322'
]);

export const corsHeaders = (request: Request) => {
  const origin = request.headers.get('origin') ?? '';
  if (!origin || !CORS_ALLOWLIST.has(origin)) return {};

  return {
    'access-control-allow-origin': origin,
    vary: 'origin',
    'access-control-allow-methods': 'POST, OPTIONS',
    'access-control-allow-headers': 'content-type, accept',
    'access-control-max-age': '86400'
  };
};

const json = (request: Request, data: unknown, status = 200) =>
  new Response(JSON.stringify(data), {
    status,
    headers: {
      'content-type': 'application/json; charset=utf-8',
      ...corsHeaders(request)
    }
  });

const normalizeKey = (value: string) => value.trim().toLowerCase();

// Twenty SELECT fields validate against the option "value" (not the label).
// We keep the form semantic (CULTSCALE, cultscale, etc) and map to the current picklist values.
const NEWSLETTER_VALUE_MAP: Record<string, string> = {
  cultscale: 'OPTION_1CULTSCALECULTSCALE',
  cultshot: 'OPTION_2',
  cultreel: 'OPTION_3',
  cultroll: 'OPTION_4'
};

const SOURCE_VALUE_MAP: Record<string, string> = {
  cultscale: 'OPTION_1',
  cultshot: 'OPTION_2',
  cultreel: 'OPTION_3',
  cultroll: 'OPTION_4'
};

const STATUS_VALUE_MAP: Record<string, string> = {
  subscribed: 'OPTION_1',
  unsubscribed: 'OPTION_2'
};

const mapPicklistValue = (map: Record<string, string>, value: string) =>
  map[normalizeKey(value)] ?? value;

type SubscribeBody = SignupBody & {
  redirectTo?: string;
};

type UnsubscribeBody = {
  token?: string;
  redirectTo?: string;
};

const safeInternalRedirect = (value: string) => {
  const v = value.trim();
  if (!v.startsWith('/')) return undefined;
  if (v.startsWith('//')) return undefined;
  return v;
};

const extractRecords = (payload: any): any[] => {
  if (!payload) return [];
  if (Array.isArray(payload)) return payload;
  if (Array.isArray(payload.data)) return payload.data;
  if (Array.isArray(payload?.data?.data)) return payload.data.data;
  if (Array.isArray(payload?.edges)) return payload.edges.map((e: any) => e?.node ?? e);
  if (Array.isArray(payload?.data?.edges)) return payload.data.edges.map((e: any) => e?.node ?? e);
  return [];
};

const findFirstId = (payload: any): string | undefined => {
  const queue: any[] = [payload];
  const seen = new Set<any>();
  let steps = 0;

  while (queue.length && steps < 2000) {
    const cur = queue.shift();
    steps++;

    if (!cur || (typeof cur !== 'object' && !Array.isArray(cur))) continue;
    if (seen.has(cur)) continue;
    seen.add(cur);

    if (typeof (cur as any).id === 'string' && (cur as any).id) return (cur as any).id;

    if (Array.isArray(cur)) {
      for (const v of cur) queue.push(v);
      continue;
    }

    for (const v of Object.values(cur)) queue.push(v);
  }

  return undefined;
};

const extractId = (payload: any): string | undefined => findFirstId(payload);

const extractAnyId = (payload: any): string | undefined => {
  const direct = findFirstId(payload);
  if (direct) return direct;
  const records = extractRecords(payload);
  return records.length ? findFirstId(records[0]) : undefined;
};

const base64UrlEncode = (bytes: Uint8Array) => {
  let str = '';
  for (const b of bytes) str += String.fromCharCode(b);
  const b64 = btoa(str);
  return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
};

const base64UrlDecode = (b64url: string) => {
  const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
  const pad = b64.length % 4 ? '='.repeat(4 - (b64.length % 4)) : '';
  const bin = atob(b64 + pad);
  const out = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
  return out;
};

const signHmacSha256 = async (secret: string, message: Uint8Array) => {
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [
    'sign'
  ]);
  const sig = await crypto.subtle.sign('HMAC', key, message);
  return new Uint8Array(sig);
};

const verifyHmacSha256 = async (secret: string, message: Uint8Array, signature: Uint8Array) => {
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [
    'verify'
  ]);
  return crypto.subtle.verify('HMAC', key, signature, message);
};

const createUnsubscribeToken = async ({
  secret,
  email,
  newsletter,
  ttlSeconds = 60 * 60 * 24 * 30
}: {
  secret: string;
  email: string;
  newsletter: string;
  ttlSeconds?: number;
}) => {
  const payload = {
    email,
    newsletter: normalizeKey(newsletter),
    exp: Math.floor(Date.now() / 1000) + ttlSeconds
  };

  const payloadBytes = new TextEncoder().encode(JSON.stringify(payload));
  const payloadB64 = base64UrlEncode(payloadBytes);
  const message = new TextEncoder().encode(payloadB64);
  const sigB64 = base64UrlEncode(await signHmacSha256(secret, message));

  return `${payloadB64}.${sigB64}`;
};

const verifyUnsubscribeToken = async (token: string, secret: string) => {
  const [payloadB64, sigB64] = token.split('.');
  if (!payloadB64 || !sigB64) return null;

  const message = new TextEncoder().encode(payloadB64);
  const signature = base64UrlDecode(sigB64);
  const ok = await verifyHmacSha256(secret, message, signature);
  if (!ok) return null;

  const payloadJson = new TextDecoder().decode(base64UrlDecode(payloadB64));
  const payload = JSON.parse(payloadJson) as { email?: string; newsletter?: string; exp?: number };
  if (!payload?.email || !payload?.newsletter || !payload?.exp) return null;
  if (payload.exp < Math.floor(Date.now() / 1000)) return null;

  return payload;
};

const restListFirst = async ({
  baseUrl,
  apiKey,
  object,
  filters
}: {
  baseUrl: string;
  apiKey: string;
  object: string;
  filters: unknown[];
}) => {
  for (const filter of filters) {
    const url = new URL(`${baseUrl}/rest/${object}`);
    url.searchParams.set('limit', '1');
    url.searchParams.set('filter', typeof filter === 'string' ? filter : JSON.stringify(filter));

    const res = await fetch(url.toString(), {
      headers: { Authorization: `Bearer ${apiKey}`, Accept: 'application/json' }
    });

    if (!res.ok) continue;
    const payload = await res.json().catch(() => null);
    const records = extractRecords(payload);
    if (records.length > 0) return records[0];
  }

  return null;
};

const restListAny = async ({
  baseUrl,
  apiKey,
  object,
  filters,
  limit
}: {
  baseUrl: string;
  apiKey: string;
  object: string;
  filters: unknown[];
  limit: number;
}) => {
  for (const filter of filters) {
    const url = new URL(`${baseUrl}/rest/${object}`);
    url.searchParams.set('limit', String(limit));
    url.searchParams.set('filter', typeof filter === 'string' ? filter : JSON.stringify(filter));

    const res = await fetch(url.toString(), {
      headers: { Authorization: `Bearer ${apiKey}`, Accept: 'application/json' }
    });

    if (!res.ok) continue;
    const payload = await res.json().catch(() => null);
    return extractRecords(payload);
  }

  return [];
};

const restListRecent = async ({
  baseUrl,
  apiKey,
  object,
  limit
}: {
  baseUrl: string;
  apiKey: string;
  object: string;
  limit: number;
}) => {
  const url = new URL(`${baseUrl}/rest/${object}`);
  url.searchParams.set('limit', String(limit));
  url.searchParams.set('sortBy', 'createdAt');
  url.searchParams.set('orderBy', 'desc');

  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${apiKey}`, Accept: 'application/json' }
  });

  if (!res.ok) return [];
  const payload = await res.json().catch(() => null);
  return extractRecords(payload);
};

const restCreate = async ({
  baseUrl,
  apiKey,
  object,
  body
}: {
  baseUrl: string;
  apiKey: string;
  object: string;
  body: Record<string, unknown>;
}) =>
  fetch(`${baseUrl}/rest/${object}`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  });

const restPatch = async ({
  baseUrl,
  apiKey,
  object,
  id,
  body
}: {
  baseUrl: string;
  apiKey: string;
  object: string;
  id: string;
  body: Record<string, unknown>;
}) =>
  fetch(`${baseUrl}/rest/${object}/${id}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  });

export async function handleSubscribe({
  request,
  env,
  defaults
}: {
  request: Request;
  env: Env;
  defaults: { newsletter: string; source: string; redirectTo?: string };
}) {
  const TWENTY_API_KEY = env.TWENTY_API_KEY;
  const TWENTY_BASE_URL = env.TWENTY_BASE_URL ?? 'https://cultscale.org';
  const SUBSCRIBE_TOKEN_SECRET = env.SUBSCRIBE_TOKEN_SECRET;
  const TURNSTILE_SECRET_KEY = env.TURNSTILE_SECRET_KEY;

  if (request.method.toUpperCase() === 'OPTIONS') return new Response(null, { status: 204, headers: corsHeaders(request) });

  if (!TWENTY_API_KEY) return json(request, { ok: false, error: 'Server misconfigured' }, 500);

  const contentType = request.headers.get('content-type') ?? '';
  const ip = request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for') ?? '';

  let firstName = '';
  let lastName = '';
  let email = '';
  let newsletter = defaults.newsletter;
  let source = defaults.source;
  let consentTextVersion = '';
  let redirectTo = defaults.redirectTo;
  let turnstileToken = '';

  try {
    if (contentType.includes('application/json')) {
      const body = (await request.json()) as SubscribeBody;
      firstName = (body.firstName ?? '').trim();
      lastName = (body.lastName ?? '').trim();
      email = (body.email ?? '').trim();
      newsletter = (body.newsletter ?? newsletter).trim() || newsletter;
      source = (body.source ?? source).trim() || source;
      consentTextVersion = (body.consentTextVersion ?? '').trim();
      redirectTo = safeInternalRedirect(body.redirectTo ?? '') ?? redirectTo;
      turnstileToken = String((body as any)?.['cf-turnstile-response'] ?? '').trim();
    } else {
      const form = await request.formData();
      firstName = String(form.get('firstName') ?? '').trim();
      lastName = String(form.get('lastName') ?? '').trim();
      email = String(form.get('email') ?? '').trim();
      newsletter = String(form.get('newsletter') ?? newsletter).trim() || newsletter;
      source = String(form.get('source') ?? source).trim() || source;
      consentTextVersion = String(form.get('consentTextVersion') ?? '').trim();
      redirectTo = safeInternalRedirect(String(form.get('redirectTo') ?? '').trim()) ?? redirectTo;
      turnstileToken = String(form.get('cf-turnstile-response') ?? '').trim();
    }
  } catch {
    return json(request, { ok: false, error: 'Invalid request' }, 400);
  }

  if (TURNSTILE_SECRET_KEY) {
    if (!turnstileToken) {
      return json(request, { ok: false, error: 'Verification required' }, 400);
    }

    const verifyForm = new FormData();
    verifyForm.append('secret', TURNSTILE_SECRET_KEY);
    verifyForm.append('response', turnstileToken);
    if (ip) verifyForm.append('remoteip', ip.split(',')[0]?.trim() || '');

    const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
      method: 'POST',
      body: verifyForm
    }).catch(() => null);

    const verifyJson = verifyRes ? await verifyRes.json().catch(() => null) : null;
    if (!verifyJson?.success) {
      return json(request, { ok: false, error: 'Verification failed' }, 400);
    }
  }

  if (!email) return json(request, { ok: false, error: 'Email required' }, 400);

  const person =
    (await restListFirst({
      baseUrl: TWENTY_BASE_URL,
      apiKey: TWENTY_API_KEY,
      object: 'people',
      filters: [
        { 'emails.primaryEmail': email },
        { 'emails.primaryEmail': { eq: email } },
        { emails: { primaryEmail: email } },
        { emails: { primaryEmail: { eq: email } } },
        `emails.primaryEmail[eq]:${email}`
      ]
    })) ??
    null;

  let personId: string | undefined = person?.id;
  let personDuplicate = false;

  if (!personId) {
    const personPayload: Record<string, unknown> = {
      emails: {
        primaryEmail: email,
        additionalEmails: []
      }
    };

    if (firstName || lastName) {
      personPayload.name = {
        firstName: firstName || undefined,
        lastName: lastName || undefined
      };
    }

    const createRes = await restCreate({ baseUrl: TWENTY_BASE_URL, apiKey: TWENTY_API_KEY, object: 'people', body: personPayload });

    if (createRes.ok) {
      const location = createRes.headers.get('location') ?? createRes.headers.get('Location') ?? '';
      const text = await createRes.text();
      let createdPayload: any = null;
      try {
        createdPayload = JSON.parse(text);
      } catch {
        createdPayload = null;
      }

      personId = extractAnyId(createdPayload);

      if (!personId && location) {
        const m = location.match(/\/people\/?([^/?#]+)$/) ?? location.match(/\/rest\/people\/([^/?#]+)/);
        personId = m?.[1];
      }

      if (!personId) {
        const existing = await restListFirst({
          baseUrl: TWENTY_BASE_URL,
          apiKey: TWENTY_API_KEY,
          object: 'people',
          filters: [
            { 'emails.primaryEmail': email },
            { 'emails.primaryEmail': { eq: email } },
            { emails: { primaryEmail: email } },
            { emails: { primaryEmail: { eq: email } } },
            `emails.primaryEmail[eq]:${email}`
          ]
        });
        personId = existing?.id;
      }

      if (!personId) {
        const recent = await restListRecent({ baseUrl: TWENTY_BASE_URL, apiKey: TWENTY_API_KEY, object: 'people', limit: 50 });
        const match =
          recent.find((p: any) => normalizeKey(String(p?.emails?.primaryEmail ?? '')) === normalizeKey(email)) ??
          null;
        personId = match?.id;
      }
    } else {
      // If the create failed due to duplicates, try to look it up again.
      const existing = await restListFirst({
        baseUrl: TWENTY_BASE_URL,
        apiKey: TWENTY_API_KEY,
        object: 'people',
        filters: [
          { 'emails.primaryEmail': email },
          { 'emails.primaryEmail': { eq: email } },
          { emails: { primaryEmail: email } },
          { emails: { primaryEmail: { eq: email } } },
          `emails.primaryEmail[eq]:${email}`
        ]
      });

      if (!existing?.id) {
        const text = await createRes.text();
        personDuplicate = /duplicate/i.test(text);

        const recent = await restListRecent({ baseUrl: TWENTY_BASE_URL, apiKey: TWENTY_API_KEY, object: 'people', limit: 50 });
        const match =
          recent.find((p: any) => normalizeKey(String(p?.emails?.primaryEmail ?? '')) === normalizeKey(email)) ??
          null;

        if (!match?.id) {
          if (personDuplicate) {
            const accept = request.headers.get('accept') ?? '';
            if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);
            return json(request, { ok: true });
          }

          return json(request, { ok: false, error: 'Upstream error', status: createRes.status, details: text }, 502);
        }

        personId = match.id;
      } else {
        personId = existing.id;
      }
    }
  }

  if (!personId && personDuplicate) {
    const accept = request.headers.get('accept') ?? '';
    if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);
    return json(request, { ok: true });
  }

  if (!personId) return json(request, { ok: false, error: 'Upstream error' }, 502);

  const consent = {
    submittedAt: new Date().toISOString(),
    ip: ip.split(',')[0]?.trim() || undefined,
    userAgent: request.headers.get('user-agent') ?? undefined,
    referer: request.headers.get('referer') ?? undefined,
    formUrl: request.url,
    textVersion: consentTextVersion || undefined
  };

  const personLabel = [firstName, lastName].filter(Boolean).join(' ') || email;
  const newsletterValue = mapPicklistValue(NEWSLETTER_VALUE_MAP, newsletter);
  const sourceValue = mapPicklistValue(SOURCE_VALUE_MAP, source);
  const statusValue = mapPicklistValue(STATUS_VALUE_MAP, 'subscribed');

  const subscriptions = await restListAny({
    baseUrl: TWENTY_BASE_URL,
    apiKey: TWENTY_API_KEY,
    object: 'subscribers',
    limit: 25,
    filters: [
      { personId },
      { personId: { eq: personId } },
      `personId[eq]:${personId}`,
      { 'person.id': personId },
      { 'person.id': { eq: personId } },
      { person: { id: personId } },
      { person: { id: { eq: personId } } }
    ]
  });

  const existingSubscription =
    subscriptions.find((s: any) => s?.newsletter === newsletterValue) ??
    subscriptions.find((s: any) => normalizeKey(String(s?.newsletter ?? '')) === normalizeKey(newsletterValue)) ??
    null;

  const baseSubscriptionUpdate: Record<string, unknown> = {
    name: `${newsletter}: ${personLabel}`,
    newsletter: newsletterValue,
    source: sourceValue,
    status: statusValue,
    consent
  };

  if (existingSubscription?.id) {
    const patchRes = await restPatch({
      baseUrl: TWENTY_BASE_URL,
      apiKey: TWENTY_API_KEY,
      object: 'subscribers',
      id: existingSubscription.id,
      body: baseSubscriptionUpdate
    });

    if (!patchRes.ok) {
      const text = await patchRes.text();
      return json(request, { ok: false, error: 'Upstream error', status: patchRes.status, details: text }, 502);
    }
  } else {
    const createRes = await restCreate({
      baseUrl: TWENTY_BASE_URL,
      apiKey: TWENTY_API_KEY,
      object: 'subscribers',
      body: {
        ...baseSubscriptionUpdate,
        personId
      }
    });

    if (!createRes.ok) {
      // race/unique constraints: list by personId then patch matching newsletter
      const retryList = await restListAny({
        baseUrl: TWENTY_BASE_URL,
        apiKey: TWENTY_API_KEY,
        object: 'subscribers',
        limit: 25,
        filters: [
          { personId },
          { personId: { eq: personId } },
          `personId[eq]:${personId}`,
          { 'person.id': personId },
          { 'person.id': { eq: personId } },
          { person: { id: personId } },
          { person: { id: { eq: personId } } }
        ]
      });

      const retry =
        retryList.find((s: any) => s?.newsletter === newsletterValue) ??
        retryList.find((s: any) => normalizeKey(String(s?.newsletter ?? '')) === normalizeKey(newsletterValue)) ??
        null;

      if (!retry?.id) {
        // Last-resort: fetch most recent records and scan. This keeps idempotency even if filters are flaky.
        const recent = await restListRecent({ baseUrl: TWENTY_BASE_URL, apiKey: TWENTY_API_KEY, object: 'subscribers', limit: 50 });
        const recentMatch =
          recent.find((s: any) => (s?.personId === personId || s?.person?.id === personId) && s?.newsletter === newsletterValue) ??
          recent.find(
            (s: any) =>
              (s?.personId === personId || s?.person?.id === personId) &&
              normalizeKey(String(s?.newsletter ?? '')) === normalizeKey(newsletterValue)
          ) ??
          null;

        if (!recentMatch?.id) {
          const text = await createRes.text();

          if ((createRes.status === 400 || createRes.status === 409) && /duplicate/i.test(text)) {
            // We couldn't locate the existing record to patch, but the subscription already exists.
            const accept = request.headers.get('accept') ?? '';
            if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);

            let unsubscribeUrl: string | undefined;
            if (SUBSCRIBE_TOKEN_SECRET) {
              const origin = new URL(request.url).origin;
              const token = await createUnsubscribeToken({ secret: SUBSCRIBE_TOKEN_SECRET, email, newsletter });
              unsubscribeUrl = `${origin}/unsubscribe?token=${encodeURIComponent(token)}`;
            }

            return json(request, { ok: true, unsubscribeUrl });
          }

          return json(request, { ok: false, error: 'Upstream error', status: createRes.status, details: text }, 502);
        }

        const patchRes = await restPatch({
          baseUrl: TWENTY_BASE_URL,
          apiKey: TWENTY_API_KEY,
          object: 'subscribers',
          id: recentMatch.id,
          body: baseSubscriptionUpdate
        });

        if (!patchRes.ok) {
          const text = await patchRes.text();
          return json(request, { ok: false, error: 'Upstream error', status: patchRes.status, details: text }, 502);
        }

        // patched successfully
        const accept = request.headers.get('accept') ?? '';
        if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);

        let unsubscribeUrl: string | undefined;
        if (SUBSCRIBE_TOKEN_SECRET) {
          const origin = new URL(request.url).origin;
          const token = await createUnsubscribeToken({ secret: SUBSCRIBE_TOKEN_SECRET, email, newsletter });
          unsubscribeUrl = `${origin}/unsubscribe?token=${encodeURIComponent(token)}`;
        }

        return json(request, { ok: true, unsubscribeUrl });
      }

      const patchRes = await restPatch({
        baseUrl: TWENTY_BASE_URL,
        apiKey: TWENTY_API_KEY,
        object: 'subscribers',
        id: retry.id,
        body: baseSubscriptionUpdate
      });

      if (!patchRes.ok) {
        const text = await patchRes.text();
        return json(request, { ok: false, error: 'Upstream error', status: patchRes.status, details: text }, 502);
      }
    }
  }

  const accept = request.headers.get('accept') ?? '';
  if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);

  let unsubscribeUrl: string | undefined;
  if (SUBSCRIBE_TOKEN_SECRET) {
    const origin = new URL(request.url).origin;
    const token = await createUnsubscribeToken({ secret: SUBSCRIBE_TOKEN_SECRET, email, newsletter });
    unsubscribeUrl = `${origin}/unsubscribe?token=${encodeURIComponent(token)}`;
  }

  return json(request, { ok: true, unsubscribeUrl });
}

export async function handleUnsubscribe({ request, env }: { request: Request; env: Env }) {
  const TWENTY_API_KEY = env.TWENTY_API_KEY;
  const TWENTY_BASE_URL = env.TWENTY_BASE_URL ?? 'https://cultscale.org';
  const SUBSCRIBE_TOKEN_SECRET = env.SUBSCRIBE_TOKEN_SECRET;

  if (request.method.toUpperCase() === 'OPTIONS') return new Response(null, { status: 204, headers: corsHeaders(request) });

  if (!TWENTY_API_KEY || !SUBSCRIBE_TOKEN_SECRET) {
    return json(request, { ok: false, error: 'Server misconfigured' }, 500);
  }

  const url = new URL(request.url);
  const contentType = request.headers.get('content-type') ?? '';

  let token = url.searchParams.get('token') ?? '';
  let redirectTo = safeInternalRedirect(url.searchParams.get('redirectTo') ?? '') ?? undefined;

  try {
    if (!token && request.method.toUpperCase() === 'POST') {
      if (contentType.includes('application/json')) {
        const body = (await request.json()) as UnsubscribeBody;
        token = (body.token ?? '').trim();
        redirectTo = safeInternalRedirect(body.redirectTo ?? '') ?? redirectTo;
      } else {
        const form = await request.formData();
        token = String(form.get('token') ?? '').trim();
        redirectTo = safeInternalRedirect(String(form.get('redirectTo') ?? '').trim()) ?? redirectTo;
      }
    }
  } catch {
    return json(request, { ok: false, error: 'Invalid request' }, 400);
  }

  if (!token) return json(request, { ok: false, error: 'Missing token' }, 400);

  const payload = await verifyUnsubscribeToken(token, SUBSCRIBE_TOKEN_SECRET);
  if (!payload) return json(request, { ok: false, error: 'Invalid or expired token' }, 400);

  const email = payload.email;
  const newsletterKey = payload.newsletter;
  const newsletterValue = mapPicklistValue(NEWSLETTER_VALUE_MAP, newsletterKey);
  const unsubscribedValue = mapPicklistValue(STATUS_VALUE_MAP, 'unsubscribed');

  const person = await restListFirst({
    baseUrl: TWENTY_BASE_URL,
    apiKey: TWENTY_API_KEY,
    object: 'people',
    filters: [
      { 'emails.primaryEmail': email },
      { 'emails.primaryEmail': { eq: email } },
      { emails: { primaryEmail: email } },
      { emails: { primaryEmail: { eq: email } } }
    ]
  });

  if (!person?.id) {
    if (redirectTo && (request.headers.get('accept') ?? '').includes('text/html')) return Response.redirect(redirectTo, 303);
    return json(request, { ok: true });
  }

  const subscription = await restListFirst({
    baseUrl: TWENTY_BASE_URL,
    apiKey: TWENTY_API_KEY,
    object: 'subscribers',
    filters: [
      { personId: person.id, newsletter: newsletterValue },
      { personId: { eq: person.id }, newsletter: newsletterValue },
      { personId: person.id, newsletter: { eq: newsletterValue } },
      { personId: { eq: person.id }, newsletter: { eq: newsletterValue } }
    ]
  });

  if (subscription?.id) {
    const ip = request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for') ?? '';
    const existingConsent = subscription.consent ?? {};
    const mergedConsent = {
      ...existingConsent,
      unsubscribe: {
        at: new Date().toISOString(),
        ip: ip.split(',')[0]?.trim() || undefined,
        userAgent: request.headers.get('user-agent') ?? undefined,
        referer: request.headers.get('referer') ?? undefined,
        url: request.url
      }
    };

    const patchRes = await restPatch({
      baseUrl: TWENTY_BASE_URL,
      apiKey: TWENTY_API_KEY,
      object: 'subscribers',
      id: subscription.id,
      body: {
        status: unsubscribedValue,
        consent: mergedConsent
      }
    });

    if (!patchRes.ok) {
      const text = await patchRes.text();
      return json(request, { ok: false, error: 'Upstream error', status: patchRes.status, details: text }, 502);
    }
  }

  const accept = request.headers.get('accept') ?? '';
  if (accept.includes('text/html') && redirectTo) return Response.redirect(redirectTo, 303);

  return json(request, { ok: true });
}

// Back-compat
export const handleNewsletterSignup = handleSubscribe;
