// InstantDevis — Moteur PDF (jsPDF) + Envoi (Cloudflare Worker optionnel) + Fallback mailto
// Aucune clé publique tierce (type EmailJS) requise dans le frontend.
// AF_MAILER s’appuie sur AF_WORKER.sendDevisJson : tente d’abord POST /v1/agents/flux puis repli 404 : /v1/send-devis
// (même base : AF_INTEGRATION.workersBase ou mailWorkerBase). Le Worker détient les secrets mail.

// ═══════════════════════════════════════════════════════════════════════
// GÉNÉRATION PDF (jsPDF) — helpers privés + coordinateur AF_PDF.generate
// ═══════════════════════════════════════════════════════════════════════

function _pdfDrawHeader(doc, devis, W, M) {
  doc.setFillColor(11, 61, 92);
  doc.rect(0, 0, W, 38, 'F');
  doc.setFillColor(255, 107, 0);
  doc.rect(W - 22, 0, 22, 38, 'F');

  doc.setTextColor(255, 255, 255);
  doc.setFontSize(20);
  doc.setFont('helvetica', 'bold');
  doc.text('InstantDevis', M, 16);
  doc.setFontSize(9);
  doc.setFont('helvetica', 'normal');
  doc.setTextColor(180, 210, 230);
  doc.text('Le devis pro, a la voix, instantanement.', M, 22);

  doc.setFillColor(255, 107, 0);
  doc.roundedRect(M, 26, 32, 8, 2, 2, 'F');
  doc.setTextColor(13, 21, 32);
  doc.setFontSize(8);
  doc.setFont('helvetica', 'bold');
  doc.text('DEVIS', M + 16, 31.2, { align: 'center' });

  doc.setTextColor(255, 255, 255);
  doc.setFontSize(11);
  doc.text(devis.id || 'Q-2026-XXX', W - M, 18, { align: 'right' });
  doc.setFontSize(8);
  doc.setFont('helvetica', 'normal');
  doc.setTextColor(180, 210, 230);
  doc.text(`Cree le ${devis.createdAt || new Date().toLocaleDateString('fr-FR')}`, W - M, 24, { align: 'right' });
}

function _pdfDrawParties(doc, prof, dout, devis, W, M, y) {
  const nom = prof.nom || 'Artisan';
  doc.setTextColor(90, 103, 114);
  doc.setFontSize(7.5);
  doc.setFont('helvetica', 'bold');
  doc.text('ARTISAN', M, y);
  doc.setTextColor(21, 32, 43);
  doc.setFontSize(11);
  doc.setFont('helvetica', 'bold');
  doc.text(nom, M, y + 6);
  doc.setFontSize(8.5);
  doc.setFont('helvetica', 'normal');
  doc.setTextColor(90, 103, 114);
  if (prof.adresse) {
    doc.text(`${prof.adresse}, ${prof.codePostal || ''} ${prof.ville || ''}`, M, y + 12);
    if (prof.tel)   doc.text(prof.tel, M, y + 17);
    if (prof.email) doc.text(prof.email, M, y + 22);
  } else {
    doc.text('contact@instantdevis.fr', M, y + 12);
    doc.text('www.instantdevis.fr', M, y + 17);
  }

  doc.setTextColor(90, 103, 114);
  doc.setFontSize(7.5);
  doc.setFont('helvetica', 'bold');
  doc.text('CLIENT', W / 2 + 10, y);
  doc.setTextColor(21, 32, 43);
  doc.setFontSize(11);
  doc.setFont('helvetica', 'bold');
  doc.text(dout.clientName || devis.clientName || 'Client', W / 2 + 10, y + 6);
  doc.setFontSize(9);
  doc.setFont('helvetica', 'normal');
  doc.setTextColor(90, 103, 114);
  const clientAddr = (dout.clientAddress && dout.clientAddress.trim()) ? dout.clientAddress : (devis.clientAddress || 'Adresse à compléter');
  doc.text(clientAddr, W / 2 + 10, y + 12);

  y += 28;
  doc.setDrawColor(230, 228, 222);
  doc.setLineWidth(0.4);
  doc.line(M, y, W - M, y);
  y += 6;

  doc.setFillColor(241, 239, 235);
  doc.roundedRect(M, y, W - M * 2, 9, 2, 2, 'F');
  doc.setTextColor(11, 61, 92);
  doc.setFontSize(10);
  doc.setFont('helvetica', 'bold');
  doc.text((dout.title && dout.title.trim()) ? dout.title : (devis.title || 'Prestation'), M + 4, y + 6);
  return y + 16;
}

function _pdfDrawLinesTable(doc, lignes, W, M, y) {
  const cols = { desc: M, qty: 118, unit: 133, pu: 148, tva: 166 };
  doc.setFillColor(11, 61, 92);
  doc.rect(M, y, W - M * 2, 7, 'F');
  doc.setTextColor(255, 255, 255);
  doc.setFontSize(7.5);
  doc.setFont('helvetica', 'bold');
  doc.text('DESCRIPTION', cols.desc + 2, y + 4.8);
  doc.text('QTE', cols.qty, y + 4.8, { align: 'right' });
  doc.text('UNITE', cols.unit + 2, y + 4.8);
  doc.text('PU HT', cols.pu + 2, y + 4.8);
  doc.text('TVA', cols.tva + 2, y + 4.8);
  doc.text('TOTAL HT', W - M, y + 4.8, { align: 'right' });
  y += 9;

  lignes.forEach((l, i) => {
    if (i % 2 === 0) {
      doc.setFillColor(248, 248, 246);
      doc.rect(M, y - 1, W - M * 2, 7.5, 'F');
    }
    const rowHT = (l.qty || l.quantite || 0) * (l.pu || l.prixUnitaire || 0);
    doc.setTextColor(21, 32, 43);
    doc.setFontSize(8.5);
    doc.setFont('helvetica', 'normal');
    const desc = l.label || l.description || '';
    doc.text(desc.length > 52 ? desc.slice(0, 49) + '...' : desc, cols.desc + 2, y + 4);
    doc.text(String(l.qty || l.quantite || 0), cols.qty, y + 4, { align: 'right' });
    doc.text(l.unit || l.unite || '', cols.unit + 2, y + 4);
    doc.text(`${(l.pu || l.prixUnitaire || 0).toFixed(2)} EUR`, cols.pu + 2, y + 4);
    doc.text(`${l.tva || l.tauxTVA || 20}%`, cols.tva + 2, y + 4);
    doc.setFont('helvetica', 'bold');
    doc.text(`${rowHT.toFixed(2)} EUR`, W - M, y + 4, { align: 'right' });
    y += 7.5;
  });

  y += 4;
  doc.setDrawColor(230, 228, 222);
  doc.line(M, y, W - M, y);
  return y + 6;
}

function _pdfDrawTotals(doc, dout, devis, lignes, W, M, y) {
  const tvaGroups = lignes.reduce((acc, l) => {
    const rate = l.tva !== undefined ? l.tva : (l.tauxTVA !== undefined ? l.tauxTVA : 20);
    acc[rate] = (acc[rate] || 0) + (l.qty || l.quantite || 0) * (l.pu || l.prixUnitaire || 0) * (rate / 100);
    return acc;
  }, {});

  let totalHT, totalTVA, totalTTC, acompte;
  if (dout.sealed) {
    ({ totalHT, totalTVA, totalTTC, acompte } = dout);
  } else {
    totalHT  = devis.totalHT  != null ? Number(devis.totalHT)  : lignes.reduce((s, l) => s + (l.qty || l.quantite || 0) * (l.pu || l.prixUnitaire || 0), 0);
    totalTVA = devis.totalTVA != null ? Number(devis.totalTVA) : Object.values(tvaGroups).reduce((a, b) => a + b, 0);
    totalTTC = devis.totalTTC != null ? Number(devis.totalTTC) : (devis.amount != null ? Number(devis.amount) : totalHT + totalTVA);
    const pct = (typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30);
    acompte = devis.acompte != null ? Number(devis.acompte) : Math.round(totalTTC * (pct / 100) * 100) / 100;
  }

  const totX = W - M - 65, totValX = W - M;
  const row = (label, value, bold = false) => {
    doc.setTextColor(bold ? 21 : 90, bold ? 32 : 103, bold ? 43 : 114);
    doc.setFont('helvetica', bold ? 'bold' : 'normal');
    doc.setFontSize(bold ? 10 : 9);
    doc.text(label, totX, y);
    doc.text(`${Number(value).toFixed(2)} EUR`, totValX, y, { align: 'right' });
    y += bold ? 7 : 5.5;
  };

  row('Total HT', totalHT);
  if (dout.sealed) {
    row('TVA (total)', totalTVA);
  } else {
    Object.entries(tvaGroups).sort(([a], [b]) => Number(a) - Number(b)).forEach(([rate, amt]) => {
      if (amt > 0) row(`TVA ${rate}%`, amt);
    });
  }

  doc.setDrawColor(11, 61, 92);
  doc.setLineWidth(0.6);
  doc.line(totX, y, totValX, y);
  y += 3;

  doc.setFillColor(11, 61, 92);
  doc.roundedRect(totX - 2, y - 2, totValX - totX + 4, 10, 2, 2, 'F');
  doc.setTextColor(255, 255, 255);
  doc.setFont('helvetica', 'bold');
  doc.setFontSize(11);
  doc.text('TOTAL TTC', totX, y + 5.5);
  doc.text(`${Number(totalTTC).toFixed(2)} EUR`, totValX, y + 5.5, { align: 'right' });
  y += 14;

  const acomptePct = (typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30);
  doc.setFillColor(255, 107, 0, 20);
  doc.roundedRect(totX - 2, y - 2, totValX - totX + 4, 8, 1.5, 1.5, 'F');
  doc.setTextColor(255, 107, 0);
  doc.setFontSize(9);
  doc.text(`Acompte ${acomptePct}% a la commande`, totX, y + 4);
  doc.setFont('helvetica', 'bold');
  doc.text(`${Number(acompte).toFixed(2)} EUR`, totValX, y + 4, { align: 'right' });
  return y + 14;
}

function _pdfDrawSignatureBlock(doc, sig, devis, W, M, y) {
  if (!sig || !sig.dataUrl) return y;
  const sigY = y + 2;
  doc.setFillColor(241, 239, 235);
  doc.roundedRect(M, sigY, W - M * 2, 38, 2, 2, 'F');
  doc.setDrawColor(11, 61, 92);
  doc.setLineWidth(0.4);
  doc.roundedRect(M, sigY, W - M * 2, 38, 2, 2, 'S');
  doc.setTextColor(11, 61, 92);
  doc.setFontSize(8);
  doc.setFont('helvetica', 'bold');
  doc.text('BON POUR ACCORD - Lu et approuve', M + 4, sigY + 6);
  doc.setTextColor(90, 103, 114);
  doc.setFont('helvetica', 'normal');
  doc.setFontSize(7.5);
  const sigDate = devis.signedAt
    ? new Date(devis.signedAt).toLocaleDateString('fr-FR')
    : (sig.date || new Date().toLocaleDateString('fr-FR'));
  doc.text(`${devis.signerName || devis.clientName || 'Client'} — Signé le ${sigDate}`, M + 4, sigY + 12);
  try { doc.addImage(sig.dataUrl, 'PNG', M + 4, sigY + 15, 80, 20); } catch (e) {}
  doc.setDrawColor(11, 61, 92);
  doc.setLineWidth(0.3);
  doc.line(M + 4, sigY + 36, M + 84, sigY + 36);
  doc.setFontSize(6.5);
  doc.setTextColor(138, 148, 160);
  doc.text('Signature du client', M + 4, sigY + 39);
  return sigY + 44;
}

function _pdfDrawHashLine(doc, devis, M, y) {
  if (!devis.signedDocumentHash) return y;
  const h = String(devis.signedDocumentHash);
  const label = 'Empreinte document (SHA-256) : ' + h;
  doc.setFontSize(6);
  doc.setFont('helvetica', 'normal');
  doc.setTextColor(90, 103, 114);
  doc.text(label.length > 95 ? label.slice(0, 92) + '...' : label, M, y + 4);
  return y + 8;
}

function _pdfDrawFooter(doc, prof, nom, W, M, pageNum, totalPages) {
  const footerY = 272;
  doc.setFillColor(241, 239, 235);
  doc.rect(0, footerY, W, 25, 'F');
  doc.setTextColor(138, 148, 160);
  doc.setFontSize(7);
  doc.setFont('helvetica', 'normal');
  const line1 = [nom, prof.entreprise].filter(Boolean).join(' — ');
  const line2 = [prof.adresse && `${prof.adresse}, ${prof.codePostal || ''} ${prof.ville || ''}`, prof.siret && `SIRET : ${prof.siret}`].filter(Boolean).join(' | ');
  const line3 = [prof.assureurNom && `Assurance : ${prof.assureurNom} N° ${prof.assuranceNum}`, prof.iban && `IBAN : ${prof.iban}`].filter(Boolean).join(' | ');
  const lineFisc = (prof.fiscalMentionShort && String(prof.fiscalMentionShort).trim()) || '';
  if (line1) doc.text(line1, M, footerY + 5);
  if (line2) doc.text(line2, M, footerY + 10);
  if (line3) {
    doc.text(line3, M, footerY + 15);
  } else {
    doc.text('Devis valable 30 jours. Conforme norme Factur-X (2026).', M, footerY + 6);
    doc.text(`Genere par InstantDevis — ${new Date().toLocaleDateString('fr-FR')}`, M, footerY + 12);
  }
  if (lineFisc) {
    doc.setFontSize(5.5);
    doc.setTextColor(120, 130, 140);
    const t = lineFisc.length > 100 ? lineFisc.slice(0, 97) + '...' : lineFisc;
    doc.text(t, M, line3 ? footerY + 19.5 : footerY + 15.5);
    doc.setFontSize(7);
  }
  doc.setTextColor(11, 61, 92);
  doc.setFont('helvetica', 'bold');
  doc.text(`Page ${pageNum} / ${totalPages}`, W - M, footerY + 17, { align: 'right' });
}

function _pdfDrawPhotosPage(doc, devis, W, M, drawFooterFn) {
  if (!devis.photos || !devis.photos.length) return 1;
  let pageNum = 2;
  doc.addPage();
  doc.setFillColor(11, 61, 92);
  doc.rect(0, 0, W, 25, 'F');
  doc.setTextColor(255, 255, 255);
  doc.setFont('helvetica', 'bold');
  doc.setFontSize(14);
  doc.text('ANNEXES PHOTOS DU CHANTIER', M, 16);

  let py = 35, px = M;
  const pWidth = (W - M * 2 - 10) / 2;
  const pHeight = pWidth * 0.75;

  devis.photos.forEach((photo) => {
    if (py + pHeight > 260) {
      drawFooterFn(pageNum);
      doc.addPage();
      pageNum++;
      py = 20;
      px = M;
    }
    try { doc.addImage(photo.data, 'JPEG', px, py, pWidth, pHeight); } catch (e) { console.warn('Erreur ajout photo PDF', e); }
    px += pWidth + 10;
    if (px + pWidth > W) { px = M; py += pHeight + 10; }
  });
  drawFooterFn(pageNum);
  return pageNum;
}

const AF_PDF = {

  generate(devis, artisanName = 'Artisan') {
    const { jsPDF } = window.jspdf;
    const doc = new jsPDF({ unit: 'mm', format: 'a4' });
    const W = 210, M = 15;
    const prof = typeof AF_PROFILE_STORE !== 'undefined' ? AF_PROFILE_STORE.get() : {};
    const nom  = prof.nom || artisanName || 'Artisan';
    const dout = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput)
      ? AF_DEVIS.documentOutput(devis)
      : { sealed: false, lignes: [], totalHT: 0, totalTVA: 0, totalTTC: Number(devis.totalTTC ?? devis.amount) || 0, acompte: devis.acompte != null ? Number(devis.acompte) : 0, clientName: String(devis.clientName || ''), clientAddress: String(devis.clientAddress || ''), title: String(devis.title || '') };
    const sigFromStore = typeof AF_SIGN_STORE !== 'undefined' && devis.id ? AF_SIGN_STORE.get(devis.id) : null;
    const sig = devis.signatureDataUrl
      ? { dataUrl: devis.signatureDataUrl, date: devis.signedAt ? new Date(devis.signedAt).toLocaleDateString('fr-FR') : new Date().toLocaleDateString('fr-FR') }
      : sigFromStore;
    const lignes = (dout.lignes && dout.lignes.length)
      ? dout.lignes
      : (devis.lignes || (Array.isArray(devis.items) && devis.items.length && typeof devis.items[0] === 'object' ? devis.items : []) || []);
    const totalPages = 1 + (devis.photos?.length ? Math.ceil(devis.photos.length / 8) : 0);
    let pageNum = 1;

    const drawFooter = (pn) => _pdfDrawFooter(doc, prof, nom, W, M, pn || pageNum, totalPages);

    _pdfDrawHeader(doc, devis, W, M);
    let y = 48;
    y = _pdfDrawParties(doc, prof, dout, devis, W, M, y);
    y = _pdfDrawLinesTable(doc, lignes, W, M, y);
    y = _pdfDrawTotals(doc, dout, devis, lignes, W, M, y);
    y = _pdfDrawSignatureBlock(doc, sig, devis, W, M, y);
    y = _pdfDrawHashLine(doc, devis, M, y);
    drawFooter(pageNum);
    pageNum = _pdfDrawPhotosPage(doc, devis, W, M, drawFooter);

    return doc;
  },

  // Téléchargement direct
  download(devis, artisanName) {
    const doc = this.generate(devis, artisanName);
    const o = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(devis) : null;
    const clientSlug = (o && o.clientName) ? o.clientName : (devis.clientName || 'client');
    doc.save(`${devis.id || 'devis'}_${String(clientSlug).replace(/\s+/g, '_')}.pdf`);
  },

  toBase64(devis, artisanName) {
    const doc = this.generate(devis, artisanName);
    return doc.output('datauristring'); // data:application/pdf;base64,...
  },
};

// ═══════════════════════════════════════════════════════════════════════
// ENVOI EMAIL
// ═══════════════════════════════════════════════════════════════════════

/** Pour logs : ne pas inclure le PDF base64. */
function _mailerRedactPayloadForLog(payload) {
  if (!payload || typeof payload !== 'object') return payload;
  const o = { ...payload };
  if (o.pdfBase64 != null) {
    const n = String(o.pdfBase64).length;
    o.pdfBase64 = n ? `[base64 omis, ${n} caractères]` : null;
  }
  return o;
}

/** Corps JSON erreur renvoyé par le Worker (HTTP non-2xx ou analyse du body). */
function _mailerParseWorkerResponseBody(wr) {
  if (wr && wr.json != null && typeof wr.json === 'object' && !Array.isArray(wr.json)) {
    return wr.json;
  }
  if (wr && wr.data != null && typeof wr.data === 'object' && !Array.isArray(wr.data)) {
    return wr.data;
  }
  if (wr && wr.body != null && String(wr.body).trim()) {
    try {
      const p = JSON.parse(String(wr.body));
      if (p && typeof p === 'object' && !Array.isArray(p)) return p;
    } catch (e) { /* ignore */ }
  }
  return null;
}

const _RESELL_DOMAIN_UX = 'Domaine e-mail non vérifié ou expéditeur non autorisé côté Resend.';

function _mailerResendDomainUserMessage(d) {
  if (!d || typeof d !== 'object') return null;
  if (d.error === 'resend_domain_or_sender') return _RESELL_DOMAIN_UX;
  if (d.provider === 'resend' && d.message && _mailerTextLooksLikeResendDomainIssue(d.message)) return _RESELL_DOMAIN_UX;
  return null;
}

function _mailerTextLooksLikeResendDomainIssue(msg) {
  const s = String(msg || '').toLowerCase();
  if (!s) return false;
  return (s.includes('domain') && (s.includes('verif') || s.includes('valid') || s.includes('not verified') || s.includes('verify')))
    || (s.includes('from') && (s.includes('not') || s.includes('verif') || s.includes('only') || s.includes('allowed')))
    || s.includes('only send')
    || (s.includes('sender') && s.includes('verif'));
}

const AF_MAILER = {

  async send(devis, toEmail, artisanName) {
    const o = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(devis) : null;
    const totalTTC = o ? o.totalTTC : (Number(devis.totalTTC != null ? devis.totalTTC : devis.amount) || 0);
    const _sendPct = (typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30);
    const acompte  = o ? o.acompte : (devis.acompte != null ? Number(devis.acompte) : Math.round(totalTTC * (_sendPct / 100) * 100) / 100);

    if (typeof AF_WORKER !== 'undefined' && navigator.onLine) {
      let pdfBase64 = null;
      try {
        const dataUri = AF_PDF.toBase64(devis, artisanName);
        const parts = String(dataUri).split(',');
        if (parts.length > 1) pdfBase64 = parts[1];
      } catch (e) {
        console.warn('[InstantDevis] PDF base64 indisponible pour le Worker', e.message);
      }
      const payload = {
        toEmail,
        artisanName: artisanName || 'Artisan',
        devis,
        totalTTC: Number(totalTTC).toFixed(2),
        acompte: Number(acompte).toFixed(2),
        pdfBase64,
      };
      if (typeof AF_PROFILE_STORE !== 'undefined') {
        try {
          payload.profile = AF_PROFILE_STORE.get();
        } catch (e) { /* ignore */ }
      }
      const wr = await AF_WORKER.sendDevisJson(payload);
      const backendJson = _mailerParseWorkerResponseBody(wr);
      const httpSt = Number(wr.httpStatus != null ? wr.httpStatus : (wr.status != null ? wr.status : 0));

      if (!wr.ok) {
        const isHttpFailure = wr.code === 'http_error';
        if (isHttpFailure) {
          let uiMsg = (wr.userMessage && String(wr.userMessage).trim()) || '';
          if (backendJson && backendJson.error === 'mail_from') {
            uiMsg = 'Configuration email manquante (MAIL_FROM côté serveur)';
          } else {
            const resendUx = _mailerResendDomainUserMessage(backendJson);
            if (resendUx) uiMsg = resendUx;
            else if (backendJson && backendJson.message != null && String(backendJson.message).trim()) {
              uiMsg = uiMsg || String(backendJson.message).trim();
            }
          }
          const backendMsg =
            (backendJson && backendJson.message != null && String(backendJson.message).trim())
            || (wr.body != null ? String(wr.body).slice(0, 800) : '')
            || uiMsg
            || 'Erreur HTTP';
          console.error('[InstantDevis] Worker mail échec (HTTP)', {
            httpStatus: httpSt,
            message: backendMsg,
            payload: _mailerRedactPayloadForLog(payload),
          });
          return {
            success: false,
            method: 'error',
            message: uiMsg || 'Envoi par le service a échoué.',
          };
        }
        if (wr.code === 'success_false' && wr.data && typeof wr.data === 'object') {
          const d = wr.data;
          const resendUx = _mailerResendDomainUserMessage(d);
          let failMsg = resendUx
            || (d.message != null && String(d.message).trim())
            || (wr.userMessage && String(wr.userMessage).trim())
            || 'Le service a refusé l’envoi.';
          if (d.error === 'mail_from') {
            failMsg = 'Configuration email manquante (MAIL_FROM côté serveur)';
          }
          console.error('[InstantDevis] Worker mail échec (success: false)', {
            httpStatus: httpSt || 200,
            message: failMsg,
            payload: _mailerRedactPayloadForLog(payload),
          });
          return {
            success: false,
            method: 'error',
            message: failMsg,
          };
        }
        if (wr && wr.skipped && wr.reason === 'no_mail_worker') {
          // Pas de base configurée : pas d’appel réseau.
        } else if (wr) {
          const bm = (backendJson && backendJson.message) || wr.userMessage || wr.error || wr.reason || '';
          console.error('[InstantDevis] Worker mail échec', {
            httpStatus: httpSt,
            message: String(bm).slice(0, 500),
            payload: _mailerRedactPayloadForLog(payload),
          });
        }
        const wh = (wr && wr.userMessage) ? String(wr.userMessage).trim() : '';
        return this._fallback(devis, toEmail, artisanName, wh ? { workerHint: wh } : undefined);
      }

      const dataOk = wr.data && typeof wr.data === 'object';
      let bodyAccepted = true;
      if (dataOk && Object.prototype.hasOwnProperty.call(wr.data, 'success')) {
        bodyAccepted = wr.data.success !== false;
      }
      if (dataOk && wr.data.skipped === true) {
        bodyAccepted = false;
      }
      const notRejected = dataOk && bodyAccepted;

      if (notRejected) {
        const sub = (wr.data.message && String(wr.data.message).trim()) || '';
        let m = sub ? `Devis envoyé — ${sub}` : 'Devis envoyé.';
        if (wr.pdfOmittedForSize && !/pdf|pièce|taille/i.test(m)) {
          m += ' (PDF non transmis, taille max dépassée côté client.)';
        }
        return {
          success: true,
          method: wr.data.method || 'worker',
          message: m,
        };
      }

      if (dataOk && wr.data.skipped === true) {
        const wh = (wr.data.message != null && String(wr.data.message).trim())
          || (wr.userMessage && String(wr.userMessage).trim())
          || '';
        if (!(wr && wr.skipped && wr.reason === 'no_mail_worker') && wr) {
          console.warn('[InstantDevis] Worker mail (skipped) :', wh || wr.reason || '');
        }
        return this._fallback(devis, toEmail, artisanName, wh ? { workerHint: wh } : undefined);
      }

      const resendUx2 = dataOk && wr.data && _mailerResendDomainUserMessage(wr.data);
      let failMsg = resendUx2
        || (dataOk && wr.data.message != null && String(wr.data.message).trim())
        || (wr.userMessage && String(wr.userMessage).trim())
        || 'Le service a refusé l’envoi.';
      if (dataOk && wr.data && wr.data.error === 'mail_from') {
        failMsg = 'Configuration email manquante (MAIL_FROM côté serveur)';
      }
      console.error('[InstantDevis] Worker mail échec (réponse JSON)', {
        httpStatus: httpSt || 200,
        message: failMsg,
        payload: _mailerRedactPayloadForLog(payload),
      });
      return {
        success: false,
        method: 'error',
        message: failMsg,
      };
    }

    return this._fallback(devis, toEmail, artisanName);
  },

  _fallback(devis, toEmail, artisanName, opts) {
    const o = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(devis) : null;
    const totalTTC = o ? o.totalTTC : (Number(devis.totalTTC != null ? devis.totalTTC : devis.amount) || 0);
    const _fbPct = (typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30);
    const acompte  = o ? o.acompte : (devis.acompte != null ? Number(devis.acompte) : Math.round(totalTTC * (_fbPct / 100) * 100) / 100);
    const presta = o && o.title ? o.title : (devis.title || '');
    const clientLine = o && o.clientName ? o.clientName : (devis.clientName || '');

    // 1. Téléchargement automatique du PDF
    try {
      AF_PDF.download(devis, artisanName);
    } catch (e) {
      console.warn('[InstantDevis] PDF download échoué:', e.message);
    }

    // 2. Ouvrir mailto pré-rempli (300ms de délai pour laisser le PDF s'enregistrer)
    setTimeout(() => {
      const subject = encodeURIComponent(`Votre devis ${devis.id} — ${presta}`);
      const body = encodeURIComponent(
        `Bonjour ${clientLine},\n\n` +
        `Veuillez trouver en pièce jointe votre devis.\n\n` +
        `Référence : ${devis.id}\n` +
        `Prestation : ${presta}\n` +
        `Total TTC  : ${Number(totalTTC).toFixed(2)} €\n` +
        `Acompte ${_fbPct}% : ${Number(acompte).toFixed(2)} €\n\n` +
        `En cas d'acceptation, merci de nous retourner le devis signé.\n\n` +
        `Cordialement,\n${artisanName || 'L\'artisan'}\n\n` +
        `— Généré par InstantDevis`
      );
      const mailto = `mailto:${toEmail || ''}?subject=${subject}&body=${body}`;
      window.open(mailto, '_blank');
    }, 300);

    const hint = opts && opts.workerHint ? String(opts.workerHint).trim() : '';
    return {
      success: false,
      method: 'fallback',
      message: 'Envoi direct impossible. PDF téléchargé + application mail ouverte.',
      ...(hint ? { workerHint: hint } : {}),
    };
  },
};

Object.assign(window, { AF_PDF, AF_MAILER });
