// ============================================================
// MVA Global Fret — Cloudflare Worker : Proxy HubSpot + double opt-in via Resend
// ============================================================
// Ce Worker gère le formulaire de contact via un flow double opt-in :
//
// 1) requestVerification : génère un token, stocke les données du formulaire en KV,
//    envoie un email de validation (lien de confirmation) via Resend.
//
// 2) verifyToken : appelé quand le client clique sur le lien de confirmation.
//    Crée le contact dans HubSpot (avec une référence générée à la volée),
//    puis envoie le welcome email avec la référence + l'adresse du dépôt Paris.
//    Idempotent : un 2ème clic ne re-crée pas de contact ni ne renvoie d'email.
//
// La référence client + l'adresse du dépôt à Paris ne fuitent jamais avant
// validation de l'email — protection anti-bot et anti-cartons-vides.
//
// ============================================================
// DÉPLOIEMENT (Phase D du plan WordPress → static)
// ============================================================
//
//   Voir cloudflare-worker/DEPLOIEMENT.md pour la procédure complète.
//
//   Secrets requis (`wrangler secret put <name>`) :
//     • HUBSPOT_TOKEN       = pat-eu1-... (read+write contacts)
//     • RESEND_API_KEY      = re_... (compte Resend partagé avec m4s-auth)
//     • RESEND_FROM_EMAIL   = adresse expéditrice (domaine vérifié chez Resend)
//     • RESEND_FROM_NAME    = nom affiché à l'expéditeur
//     • PARIS_DEPOT_ADDRESS = adresse complète du dépôt Paris
//     • TURNSTILE_SECRET    = secret Cloudflare Turnstile (anti-bot)
//     • SITE_URL            = base URL du site (ex: "https://mva-globalfret.com")
//
//   Bindings KV requis :
//     • WELCOME_KV → namespace `mva-welcome-tracker` (idempotence verifyToken)
//
// ============================================================

const HUBSPOT_API = 'https://api.hubapi.com';

const corsHeaders = {
  'Access-Control-Allow-Origin' : '*',
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
};

export default {
  // -----------------------------------------------------------
  // 1) Handler navigateur (POST depuis le formulaire / page de confirmation)
  // -----------------------------------------------------------
  async fetch(request, env) {

    if (request.method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }
    if (request.method !== 'POST') {
      return new Response('Method Not Allowed', { status: 405 });
    }

    const token = env.HUBSPOT_TOKEN;
    if (!token) {
      return jsonResponse({ error: 'HUBSPOT_TOKEN env var not set' }, 500);
    }

    try {
      const body = await request.json();
      const { email, action } = body;

      // ── action: nextRef ─────────────────────────────────────
      if (action === 'nextRef') {
        return jsonResponse({ nextRef: await getNextRef(token) });
      }

      // ── action: requestVerification ──────────────────────────
      // Génère un token unique, stocke TOUTES les données du formulaire en KV,
      // et envoie un email de validation via Resend. Le contact n'est créé
      // dans HubSpot QU'APRÈS clic sur le lien de confirmation (anti-spam :
      // les inscriptions non vérifiées ne polluent pas le CRM).
      // Anti-bot : Turnstile vérifié d'abord.
      if (action === 'requestVerification') {
        if (!body.email) return jsonResponse({ error: 'email requis' }, 400);

        const turnstileOk = await verifyTurnstile(env, body.turnstile_token, request);
        if (!turnstileOk) {
          return jsonResponse({ ok: false, error: 'Turnstile validation failed' }, 403);
        }

        try {
          const verToken = crypto.randomUUID().replace(/-/g, '');
          const tokenData = {
            firstname : body.firstname || '',
            lastname  : body.lastname  || '',
            phone     : body.phone     || '',
            email     : body.email.toLowerCase().trim(),
            address   : body.address   || '',
            createdAt : new Date().toISOString(),
          };

          if (!env.WELCOME_KV) {
            return jsonResponse({ ok: false, error: 'KV not bound' }, 500);
          }
          // Token valide 24h
          await env.WELCOME_KV.put(`verify:${verToken}`, JSON.stringify(tokenData), {
            expirationTtl: 60 * 60 * 24,
          });

          await sendVerificationEmail(env, tokenData, verToken);
          return jsonResponse({ ok: true });
        } catch (err) {
          return jsonResponse({ ok: false, error: err.message }, 500);
        }
      }

      // ── action: verifyToken ──────────────────────────────────
      // Appelé par confirmation.html quand l'utilisateur clique sur
      // le lien dans l'email de validation. C'est ICI que le contact
      // est CRÉÉ dans HubSpot (avec une référence générée à la volée),
      // puis le welcome email est envoyé (ref + adresse Paris).
      // Idempotent : un 2ème clic ne re-crée pas de contact.
      if (action === 'verifyToken') {
        if (!body.token) return jsonResponse({ error: 'token requis' }, 400);
        if (!env.WELCOME_KV) return jsonResponse({ ok: false, error: 'KV not bound' }, 500);

        const key = `verify:${body.token}`;
        const raw = await env.WELCOME_KV.get(key);
        if (!raw) {
          return jsonResponse({ ok: false, error: 'Token invalide ou expiré' }, 404);
        }
        const tokenData = JSON.parse(raw);

        // Idempotence : si déjà consommé, retourne le résultat précédent
        // sans recréer le contact ni renvoyer d'email.
        if (tokenData.used) {
          return jsonResponse({
            ok: true,
            firstname        : tokenData.firstname,
            reference_client : tokenData.reference_client || '',
          });
        }

        try {
          // 1) Récupère la ref existante si le contact est déjà dans HubSpot
          //    (réinscription après suppression d'un test, ou création via
          //    l'ancien flow Forms API). Sinon génère la prochaine ref.
          let refNumber;
          try {
            const existing = await searchContactByEmail(token, tokenData.email);
            const existingResult = (existing.results || [])[0];
            const existingRef = existingResult?.properties?.reference_client;
            refNumber = existingRef || await getNextRef(token);
          } catch (_) {
            // Si la search échoue (scope manquant, etc.), fallback : génère
            // une nouvelle ref. Le Forms API gérera la dédup côté HubSpot.
            refNumber = await getNextRef(token);
          }

          // 2) Création directe via CRM API (= more deterministic que Forms API
          //    qui peut accepter une submission sans réellement créer le contact
          //    à cause des filtres anti-spam ou de la config du Form HubSpot).
          //    Requires scope crm.objects.contacts.write.
          //    En cas de 409 (contact déjà existant), fallback sur PATCH par ID
          //    pour update les propriétés (= notamment reference_client).
          const crmRes = await fetch(
            `${HUBSPOT_API}/crm/v3/objects/contacts`,
            {
              method: 'POST',
              headers: {
                'Content-Type' : 'application/json',
                'Authorization': `Bearer ${token}`,
              },
              body: JSON.stringify({
                properties: {
                  firstname        : tokenData.firstname || '',
                  lastname         : tokenData.lastname  || '',
                  phone            : tokenData.phone     || '',
                  email            : tokenData.email,
                  address          : tokenData.address   || '',
                  reference_client : refNumber,
                },
              }),
            }
          );
          if (crmRes.status === 409) {
            // Contact existe déjà — update via PATCH par ID
            const search = await searchContactByEmail(token, tokenData.email);
            const existing = (search.results || [])[0];
            if (existing?.id) {
              const patchRes = await fetch(
                `${HUBSPOT_API}/crm/v3/objects/contacts/${existing.id}`,
                {
                  method: 'PATCH',
                  headers: {
                    'Content-Type' : 'application/json',
                    'Authorization': `Bearer ${token}`,
                  },
                  body: JSON.stringify({
                    properties: {
                      firstname        : tokenData.firstname || existing.properties?.firstname || '',
                      lastname         : tokenData.lastname  || existing.properties?.lastname  || '',
                      phone            : tokenData.phone     || existing.properties?.phone     || '',
                      address          : tokenData.address   || '',
                      reference_client : refNumber,
                    },
                  }),
                }
              );
              if (!patchRes.ok) {
                const errTxt = await patchRes.text();
                throw new Error(`HubSpot CRM patch failed ${patchRes.status}: ${errTxt.slice(0, 200)}`);
              }
            }
          } else if (!crmRes.ok) {
            const errTxt = await crmRes.text();
            throw new Error(`HubSpot CRM create failed ${crmRes.status}: ${errTxt.slice(0, 200)}`);
          }

          // 3) Envoie le welcome email avec ref + adresse Paris
          const welcomeContact = { ...tokenData, reference_client: refNumber };
          await sendWelcomeViaResend(env, welcomeContact);

          // 4) Marque le token consommé (gardé 7j pour idempotence)
          await env.WELCOME_KV.put(key, JSON.stringify({
            ...tokenData,
            used             : true,
            usedAt           : new Date().toISOString(),
            reference_client : refNumber,
          }), {
            expirationTtl: 60 * 60 * 24 * 7,
          });

          return jsonResponse({
            ok: true,
            firstname        : tokenData.firstname,
            reference_client : refNumber,
          });
        } catch (err) {
          return jsonResponse({ ok: false, error: err.message }, 500);
        }
      }

      // ── action: sendWelcomeBack ─────────────────────────────
      // Envoie un email "Vous êtes déjà inscrit" au client qui tente
      // une ré-inscription. Idempotent côté HubSpot (= aucune création
      // ni update de contact). Anti-bot via Turnstile + sanity check
      // que l'email existe vraiment dans HubSpot avant d'envoyer.
      if (action === 'sendWelcomeBack') {
        if (!body.email) return jsonResponse({ error: 'email requis' }, 400);

        const turnstileOk = await verifyTurnstile(env, body.turnstile_token, request);
        if (!turnstileOk) {
          return jsonResponse({ ok: false, error: 'Turnstile validation failed' }, 403);
        }

        try {
          // Vérification : le contact existe bien (= prevent spam vers
          // emails inconnus en passant un faux turnstile)
          const search = await searchContactByEmail(token, body.email);
          const existing = (search.results || [])[0];
          if (!existing) {
            return jsonResponse({ ok: false, error: 'Contact not found' }, 404);
          }

          await sendWelcomeBackViaResend(env, {
            firstname        : body.firstname || existing.properties?.firstname || '',
            email            : body.email,
            reference_client : existing.properties?.reference_client || '',
          });
          return jsonResponse({ ok: true });
        } catch (err) {
          return jsonResponse({ ok: false, error: err.message }, 500);
        }
      }

      // ── action par défaut : vérification doublon par email ──
      if (!email || typeof email !== 'string') {
        return jsonResponse({ error: 'Email requis' }, 400);
      }

      const data = await searchContactByEmail(token, email);
      return jsonResponse(data);

    } catch (err) {
      return jsonResponse({ error: err.message }, 500);
    }
  },
};

// =============================================================
// HubSpot : recherches & lectures
// =============================================================

async function searchContactByEmail(token, email) {
  const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify({
      filterGroups: [{
        filters: [{ propertyName: 'email', operator: 'EQ', value: email.toLowerCase().trim() }],
      }],
      properties: ['firstname', 'lastname', 'email', 'reference_client'],
    }),
  });

  if (!res.ok) {
    throw new Error(`HubSpot lookup failed: ${res.status}`);
  }
  return res.json();
}

async function getNextRef(token) {
  // Paginate through ALL HubSpot contacts with `reference_client` property to
  // find the true numeric maximum. Previous version used `limit: 100` without
  // pagination — produced collisions once the contact count exceeded 100
  // because HubSpot search results don't guarantee ordering by ref. With
  // pagination, we walk the full set: 100 per page × N pages until no more.
  // For ~1000 contacts = 10 API calls. Acceptable cost given that this runs
  // once per signup confirmation (= rare path).
  let maxNum = 0;
  let after; // undefined on first iteration
  do {
    const body = {
      filterGroups: [{
        filters: [{ propertyName: 'reference_client', operator: 'HAS_PROPERTY' }],
      }],
      properties: ['reference_client'],
      limit: 100,
    };
    if (after) body.after = after;

    const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
      },
      body: JSON.stringify(body),
    });
    if (!res.ok) {
      throw new Error(`HubSpot search failed: ${res.status}`);
    }
    const data = await res.json();
    (data.results || []).forEach(c => {
      const m = (c.properties?.reference_client || '').match(/^MVA-(\d+)$/);
      if (m) {
        const n = parseInt(m[1], 10);
        if (n > maxNum) maxNum = n;
      }
    });
    after = data.paging?.next?.after;
  } while (after);
  return 'MVA-' + String(maxNum + 1).padStart(3, '0');
}

// =============================================================
// Resend : envoi d'emails transactionnels (verification + welcome)
// =============================================================
// Aligné avec m4s-auth (Phase 2.1) qui utilise déjà Resend en production.
// Le compte Resend (et le domaine vérifié) sont partagés entre m4s-auth et
// ce Worker — un seul fournisseur SMTP pour tout Mind4Solutions.
//
// Setup requis (`wrangler secret put <name>`) :
//   - env.RESEND_API_KEY     = clé API Resend (re_...)
//   - env.RESEND_FROM_EMAIL  = adresse expéditrice (domaine vérifié chez Resend)
//   - env.RESEND_FROM_NAME   = nom affiché à l'expéditeur (ex: "MVA Global Fret")
//   - env.SITE_URL           = base URL du site (ex: "https://mva-globalfret.com")
//
// API doc : https://resend.com/docs/api-reference/emails/send-email

const RESEND_API = 'https://api.resend.com/emails';

async function resendSend(env, { to, subject, html }) {
  if (!env.RESEND_API_KEY) {
    throw new Error('RESEND_API_KEY env var not set');
  }
  const fromEmail = env.RESEND_FROM_EMAIL || 'noreply@mva-globalfret.com';
  const fromName  = env.RESEND_FROM_NAME  || 'MVA Global Fret';

  const res = await fetch(RESEND_API, {
    method: 'POST',
    headers: {
      'Content-Type' : 'application/json',
      'Authorization': `Bearer ${env.RESEND_API_KEY}`,
    },
    body: JSON.stringify({
      from   : `${fromName} <${fromEmail}>`,
      to     : [to],
      subject: subject,
      html   : html,
    }),
  });
  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Resend ${res.status}: ${text}`);
  }
  return res.json();
}

async function sendVerificationEmail(env, contact, verToken) {
  const siteUrl = env.SITE_URL || 'https://mva-globalfret.com';
  const verifyUrl = `${siteUrl}/confirmation.html?token=${verToken}`;
  const logoUrl = `${siteUrl}/PNG%20MVA%20GLOBAL%20FRET.png`;
  const firstname = escapeHtml(contact.firstname || '');

  const html = `<!DOCTYPE html>
<html lang="fr">
<body style="margin:0;padding:0;font-family:Arial,sans-serif;background:#f5f5f5;">
  <div style="max-width:600px;margin:0 auto;background:#fff;">
    <div style="background:#1a1a3e;padding:24px 30px;">
      <table role="presentation" cellspacing="0" cellpadding="0" border="0" style="width:100%;border-collapse:collapse;">
        <tr>
          <td style="width:80px;vertical-align:middle;">
            <img src="${logoUrl}" alt="MVA Global Fret" style="display:block;width:70px;height:auto;border:0;">
          </td>
          <td style="vertical-align:middle;text-align:center;padding-right:80px;">
            <div style="color:#c5a55a;font-size:24px;font-weight:700;letter-spacing:2px;">MVA GLOBAL FRET</div>
            <div style="color:#fff;font-size:13px;margin-top:6px;">Fret Aérien Paris — Antananarivo</div>
          </td>
        </tr>
      </table>
    </div>
    <div style="padding:40px;">
      <p style="font-size:18px;color:#1a1a3e;font-weight:bold;">Bonjour ${firstname},</p>
      <p style="color:#333;line-height:1.6;">
        Merci pour votre inscription chez <strong>MVA Global Fret</strong> !
      </p>
      <p style="color:#333;line-height:1.6;">
        Pour finaliser votre inscription et recevoir votre <strong>numéro de référence client</strong>
        ainsi que <strong>l'adresse de notre dépôt à Paris</strong>, cliquez sur le bouton ci-dessous :
      </p>
      <div style="text-align:center;margin:32px 0;">
        <a href="${verifyUrl}" style="display:inline-block;background:#c5a55a;color:#1a1a3e;padding:16px 40px;border-radius:50px;text-decoration:none;font-weight:700;font-size:16px;letter-spacing:0.5px;">
          ✓ Confirmer mon email
        </a>
      </div>
      <p style="color:#666;font-size:13px;line-height:1.6;">
        Ce lien est valable <strong>24 heures</strong>. Si vous n'êtes pas à l'origine de cette inscription, ignorez simplement cet email.
      </p>
      <p style="color:#666;font-size:12px;line-height:1.6;border-top:1px solid #eee;padding-top:18px;margin-top:30px;">
        Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :<br>
        <span style="color:#c5a55a;word-break:break-all;">${verifyUrl}</span>
      </p>
    </div>
    <div style="background:#1a1a3e;padding:18px;text-align:center;color:#c5a55a;font-size:12px;">
      © 2026 MVA Global Fret — Tous droits réservés
    </div>
  </div>
</body>
</html>`;

  return resendSend(env, {
    to: contact.email,
    subject: 'Confirmez votre inscription chez MVA Global Fret',
    html,
  });
}

async function sendWelcomeViaResend(env, contact) {
  const siteUrl = env.SITE_URL || 'https://mva-globalfret.com';
  const logoUrl = `${siteUrl}/PNG%20MVA%20GLOBAL%20FRET.png`;
  const firstname = escapeHtml(contact.firstname || '');
  const ref = escapeHtml(contact.reference_client || '');
  const refRaw = contact.reference_client || '';

  // Format adresse Paris : la 1ère ligne (nom du destinataire) reçoit
  // automatiquement la référence client entre parenthèses, comme ça
  // le client a directement la bonne forme à recopier sur son colis.
  // Support aussi un placeholder {{ref}} si présent dans l'env var.
  let parisAddrRaw = env.PARIS_DEPOT_ADDRESS || '';
  if (parisAddrRaw.includes('{{ref}}')) {
    parisAddrRaw = parisAddrRaw.replace(/\{\{ref\}\}/g, refRaw);
  } else if (refRaw && parisAddrRaw) {
    const lines = parisAddrRaw.split('\n');
    lines[0] = `${lines[0]} (${refRaw})`;
    parisAddrRaw = lines.join('\n');
  }
  const parisAddr = escapeHtml(parisAddrRaw).replace(/\n/g, '<br>');

  const html = `<!DOCTYPE html>
<html lang="fr">
<body style="margin:0;padding:0;font-family:Arial,sans-serif;background:#f5f5f5;">
  <div style="max-width:600px;margin:0 auto;background:#fff;">
    <div style="background:#1a1a3e;padding:24px 30px;">
      <table role="presentation" cellspacing="0" cellpadding="0" border="0" style="width:100%;border-collapse:collapse;">
        <tr>
          <td style="width:80px;vertical-align:middle;">
            <img src="${logoUrl}" alt="MVA Global Fret" style="display:block;width:70px;height:auto;border:0;">
          </td>
          <td style="vertical-align:middle;text-align:center;padding-right:80px;">
            <div style="color:#c5a55a;font-size:24px;font-weight:700;letter-spacing:2px;">MVA GLOBAL FRET</div>
            <div style="color:#fff;font-size:13px;margin-top:6px;">Fret Aérien Paris — Antananarivo</div>
          </td>
        </tr>
      </table>
    </div>
    <div style="padding:40px;">
      <p style="font-size:18px;color:#1a1a3e;font-weight:bold;">Bonjour ${firstname},</p>
      <p style="color:#333;line-height:1.6;">
        Bienvenu(e) chez <strong>MVA Global Fret</strong> ! Votre email est confirmé,
        votre inscription est désormais active.
      </p>
      <div style="background:#f0ead8;border-left:4px solid #c5a55a;padding:16px 20px;margin:24px 0;border-radius:4px;">
        <p style="margin:0;color:#1a1a3e;font-size:14px;">Votre numéro de référence client :</p>
        <p style="margin:8px 0 0;color:#1a1a3e;font-size:22px;font-weight:bold;letter-spacing:2px;">${ref}</p>
        <p style="margin:6px 0 0;color:#666;font-size:12px;">Conservez ce numéro précieusement.</p>
      </div>
      <p style="color:#333;margin-top:28px;"><strong>L'adresse à Paris pour l'envoi de vos colis :</strong></p>
      <div style="background:#f9f9f9;border:1px solid #ddd;padding:20px 24px;border-radius:6px;margin:12px 0;font-family:monospace;font-size:15px;line-height:1.8;color:#1a1a3e;">
        ${parisAddr}
      </div>
      <div style="background:#fff3cd;border:1px solid #ffc107;padding:14px 18px;border-radius:6px;margin:16px 0;">
        <p style="margin:0 0 8px;color:#856404;font-size:14px;font-weight:bold;">
          ⚠️ Important : ne modifiez rien à ces informations.
        </p>
        <p style="margin:0;color:#856404;font-size:14px;line-height:1.5;">
          Recopiez l'adresse <strong>exactement telle qu'elle est indiquée ci-dessus</strong>,
          sans rien retirer ni ajouter. Votre numéro de référence <strong>${ref}</strong>
          fait partie intégrante de l'adresse — c'est ce qui garantit que votre colis nous arrive bien.
        </p>
      </div>
      <p style="color:#333;line-height:1.6;">
        Pour toute question, contactez-nous :<br>
        📧 mvaglobalfret@gmail.com<br>
        📞 +33 7 80 97 08 25 (France) — +261 38 49 737 51 (Madagascar)
      </p>
    </div>
    <div style="background:#1a1a3e;padding:18px;text-align:center;color:#c5a55a;font-size:12px;">
      © 2026 MVA Global Fret — Tous droits réservés
    </div>
  </div>
</body>
</html>`;

  return resendSend(env, {
    to: contact.email,
    subject: `Bienvenue chez MVA Global Fret — Votre référence ${ref}`,
    html,
  });
}

// Email "Ravis de vous revoir" pour les clients déjà inscrits qui retentent
// le formulaire de contact. Reprend EXACTEMENT le template original (= avant
// migration EmailJS \xe2\x86\x92 Resend) car son contenu est strat\xc3\xa9gique \xe2\x80\x94 rappel
// adresse Paris + warning anti-modification + r\xe9f\xe9rence client. Seules
// modifications : footer (c) 2025 \xe2\x86\x92 \xc2\xa9 2026, suppression du tag
// "Email sent via EmailJS.com" (obsol\xe8te depuis Resend), URL logo
// pointe vers le nouveau domaine, et adresse Paris injecte la ref via
// le placeholder {{ref}} de PARIS_DEPOT_ADDRESS.
//
// Idempotent c\xf4t\xe9 HubSpot (= z\xe9ro write).
async function sendWelcomeBackViaResend(env, contact) {
  const siteUrl = env.SITE_URL || 'https://mva-globalfret.com';
  const logoUrl = `${siteUrl}/PNG%20MVA%20GLOBAL%20FRET.png`;
  const firstnameRaw = contact.firstname || '';
  const firstname = escapeHtml(firstnameRaw);
  const refRaw = contact.reference_client || '';
  const ref = escapeHtml(refRaw);

  // Construction adresse Paris (= m\xeame logique que sendWelcomeViaResend) :
  // injecte la ref client soit via placeholder {{ref}}, soit en l'ajoutant
  // entre parenth\xe8ses sur la 1\xe8re ligne (= pattern original "VASTA Mélissa (MVA-XXX)").
  let parisAddrRaw = env.PARIS_DEPOT_ADDRESS || '';
  if (parisAddrRaw.includes('{{ref}}')) {
    parisAddrRaw = parisAddrRaw.replace(/\{\{ref\}\}/g, refRaw);
  } else if (refRaw && parisAddrRaw) {
    const lines = parisAddrRaw.split('\n');
    lines[0] = `${lines[0]} (${refRaw})`;
    parisAddrRaw = lines.join('\n');
  }
  // 1\xe8re ligne en gras (= match original `<strong>VASTA Melissa (MVA-XXX)</strong>`)
  const addrLines = escapeHtml(parisAddrRaw).split('\n');
  const parisAddrHtml = addrLines.length > 1
    ? `<strong>${addrLines[0]}</strong><br>${addrLines.slice(1).join('<br>')}`
    : escapeHtml(parisAddrRaw);

  const greetingTitle = firstnameRaw
    ? `Ravis de vous revoir, ${firstname} !`
    : 'Ravis de vous revoir !';

  const html = `<html lang=""><body><div style="font-family:Arial,sans-serif;font-size:16px;background-color:#f5f5f5;padding:20px">
  <div style="max-width:600px;margin:auto;background-color:#ffffff;border-radius:8px;overflow:hidden">
    <div style="background-color:#1a1a3e;padding:30px 40px;text-align:center">
      <table width="100%" cellpadding="0" cellspacing="0" role="presentation"><tr><td width="145" style="padding:15px 0 15px 20px;vertical-align:middle"><img src="${logoUrl}" width="130" height="130" alt="MVA" style="display:block;"></td><td style="text-align:center;padding:15px 75px 15px 0;vertical-align:middle"><div style="color:#c5a55a;font-size:22px;font-weight:700;letter-spacing:2px;font-family:Arial,sans-serif">MVA GLOBAL FRET</div><div style="color:#ffffff;font-size:12px;margin-top:4px;font-family:Arial,sans-serif">Fret Aerien Paris - Antananarivo</div></td></tr></table>
    </div>
    <div style="padding:40px">
      <p style="color:#1a1a3e;font-size:22px;font-weight:bold;margin-top:0">${greetingTitle}</p>
      <p style="color:#333333">Nous avons bien recu votre nouvelle tentative d&#39;inscription. Pas d&#39;inquietude : vous etes <strong>deja client</strong> chez MVA Global Fret !</p>
      <p style="color:#333333">Voici un rappel de votre numero de reference client :</p>
      <div style="background-color:#f0ead8;border-left:4px solid #c5a55a;padding:16px 20px;margin:24px 0;border-radius:4px;text-align:center">
        <p style="margin:0;color:#1a1a3e;font-size:14px;letter-spacing:1px">VOTRE NUMERO DE REFERENCE CLIENT</p>
        <p style="margin:8px 0 0 0;color:#1a1a3e;font-size:28px;font-weight:bold;letter-spacing:2px">${ref}</p>
        <p style="margin:6px 0 0 0;color:#666666;font-size:12px">Conservez ce numero precieusement.</p>
      </div>
      <p style="color:#333333;margin-top:28px"><strong>L&#39;adresse a Paris pour l&#39;envoi de vos colis est :</strong></p>
      <div style="background-color:#f9f9f9;border:1px solid #dddddd;padding:20px 24px;border-radius:6px;margin:12px 0 24px 0;font-family:monospace;font-size:15px;line-height:1.8;color:#1a1a3e">
        ${parisAddrHtml}
      </div>
      <div style="background-color:#fff3cd;border:1px solid #ffc107;padding:16px 20px;border-radius:6px;margin:24px 0">
        <p style="margin:0;color:#856404;font-size:14px"><strong>IMPORTANT :</strong> Cette adresse ne doit etre changee sous aucun pretexte. Toute modification empecherait la bonne transmission de votre colis a notre depot a Paris.</p>
      </div>
      <p style="color:#333333">Pour toute question, n&#39;hesitez pas a nous contacter :</p>
      <ul style="color:#333333;line-height:2">
        <li><a href="mailto:mvaglobalfret@gmail.com" style="color:#c5a55a">mvaglobalfret@gmail.com</a></li>
        <li><a href="tel:+33780970825" style="color:#c5a55a">+33 7 80 97 08 25</a> (France)</li>
        <li><a href="tel:+261384973751" style="color:#c5a55a">+261 38 49 737 51</a> (Madagascar)</li>
      </ul>
      <p style="color:#333333;margin-top:32px">A tres bientot pour votre prochain envoi,<br><strong>L&#39;equipe MVA Global Fret</strong></p>
    </div>
    <div style="background-color:#1a1a3e;color:rgba(255,255,255,0.6);padding:16px;text-align:center;font-size:12px">
      (c) 2026 MVA Global Fret - Antananarivo 101, Madagascar
    </div>
  </div>
</div></body></html>`;

  // Subject : reprend strictement le sujet original "Ravis de vous revoir, [firstname] !"
  // (= en cas de firstname vide, fallback sans virgule).
  const subjectFirstname = firstnameRaw.replace(/[\r\n]/g, '').trim();
  return resendSend(env, {
    to: contact.email,
    subject: subjectFirstname
      ? `Ravis de vous revoir, ${subjectFirstname} !`
      : 'Ravis de vous revoir !',
    html,
  });
}

function escapeHtml(s) {
  return String(s)
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}

// =============================================================
// Cloudflare Turnstile : validation anti-bot
// =============================================================
// Reçoit le token généré côté client (window.turnstileToken) et
// l'envoie à l'API Cloudflare avec le secret pour validation.
// Renvoie true uniquement si Cloudflare confirme que c'est un
// utilisateur humain.
async function verifyTurnstile(env, token, request) {
  if (!token) return false;
  if (!env.TURNSTILE_SECRET) {
    // En dev / si pas configuré, on laisse passer (à durcir en prod)
    console.warn('TURNSTILE_SECRET not set, skipping validation');
    return true;
  }
  const ip = request.headers.get('CF-Connecting-IP') || '';
  const formData = new FormData();
  formData.append('secret', env.TURNSTILE_SECRET);
  formData.append('response', token);
  if (ip) formData.append('remoteip', ip);

  try {
    const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
      method: 'POST',
      body: formData,
    });
    const data = await res.json();
    return data.success === true;
  } catch (err) {
    console.warn('Turnstile verification error:', err);
    return false;
  }
}

// =============================================================
// Helpers
// =============================================================

function jsonResponse(data, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { ...corsHeaders, 'Content-Type': 'application/json' },
  });
}
