// InstantDevis — Écrans secondaires
// Liste devis, clients, détail, paramètres

// ──────────────────────────────────────────────────────────────
// LISTE DE TOUS LES DEVIS (navigation « Devis »)
// ──────────────────────────────────────────────────────────────
function AfPaiements({ t, currency, setView }) {
  const [filter, setFilter] = React.useState('all');
  const [quotes, setQuotes] = React.useState(() => AF_STORE.getAll());

  React.useEffect(() => {
    setQuotes(AF_STORE.getAll());
    const u = AF_STORE.subscribe(() => setQuotes(AF_STORE.getAll()));
    return () => u();
  }, []);

  const statuts = [
    { id: 'all', label: 'Tous' },
    { id: 'sent', label: 'Envoyés' },
    { id: 'signe', label: 'Signés' },
    { id: 'retard', label: 'Retard' },
    { id: 'paye', label: 'Payés' },
  ];

  const filtered = filter === 'all'
    ? quotes
    : quotes.filter(q => {
      const st = String(q.status || '').toLowerCase();
      if (filter === 'sent') return st === 'sent' || st === 'envoye';
      return st === filter;
    });

  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflowY: 'auto', background: t.bg }}>
      <AfScreenHeader title="Mes devis" subtitle={`${quotes.length} devis`} t={t} tape />

      {/* Filtres */}
      <div style={{
        display: 'flex', gap: 6, padding: '10px 16px',
        overflowX: 'auto', flexShrink: 0,
        borderBottom: `1px solid ${t.border}`,
      }}>
        {statuts.map(s => (
          <button
            key={s.id}
            onClick={() => setFilter(s.id)}
            style={{
              flexShrink: 0, padding: '6px 12px', borderRadius: 20,
              background: filter === s.id ? t.primary : t.bgElevated,
              color: filter === s.id ? '#fff' : t.textMuted,
              border: `1px solid ${filter === s.id ? t.primary : t.border}`,
              fontSize: 11, fontWeight: 700,
              cursor: 'pointer', whiteSpace: 'nowrap',
            }}
          >
            {s.label}
          </button>
        ))}
      </div>

      {/* Liste */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '12px 16px 24px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {filtered.length === 0 && (
          <div style={{
            background: t.bgElevated,
            borderRadius: 10,
            border: `1px dashed ${t.border}`,
            padding: '22px 16px',
            textAlign: 'center',
          }}>
            <div style={{ fontSize: 14, fontWeight: 800, color: t.text, marginBottom: 6 }}>
              Aucun devis pour le moment
            </div>
            <div style={{ fontSize: 12, color: t.textMuted, marginBottom: 14, lineHeight: 1.45 }}>
              Les devis créés via Instant Note apparaîtront ici.
            </div>
            <button
              type="button"
              onClick={() => setView('note-flash')}
              style={{
                padding: '12px 18px',
                borderRadius: 10,
                background: `linear-gradient(135deg, ${t.safety} 0%, #ff8c00 100%)`,
                border: 'none',
                fontSize: 13,
                fontWeight: 800,
                color: '#0D1520',
                cursor: 'pointer',
                boxShadow: `0 4px 14px rgba(255,107,0,0.35)`,
              }}
            >
              Créer un devis avec Instant Note
            </button>
          </div>
        )}
        {filtered.map(q => {
          const trade = AF_TRADE_META[q.trade] || AF_TRADE_META.multi;
          const od = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(q) : null;
          const amt = od ? od.totalTTC : (q.totalTTC != null ? q.totalTTC : q.amount);
          const _acomptePct = typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30;
          const deposit = od ? od.acompte : (q.acompte != null ? q.acompte : Math.round((Number(amt) || 0) * (_acomptePct / 100) * 100) / 100);
          return (
            <div
              key={q.id}
              onClick={() => setView('detail', q.id)}
              style={{
                background: t.bgElevated, borderRadius: 10,
                border: `1px solid ${q.status === 'retard' ? 'rgba(255,107,0,0.4)' : t.border}`,
                padding: '12px 14px', cursor: 'pointer',
                boxShadow: t.shadowSm,
                transition: 'transform 0.1s',
              }}
              onMouseDown={e => e.currentTarget.style.transform = 'scale(0.99)'}
              onMouseUp={e => e.currentTarget.style.transform = 'scale(1)'}
              onMouseLeave={e => e.currentTarget.style.transform = 'scale(1)'}
            >
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontSize: 13, fontWeight: 700, color: t.text,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>
                    {q.title}
                  </div>
                  <div style={{ fontSize: 11, color: t.textMuted, marginTop: 2 }}>
                    {q.clientName}
                    <span style={{
                      marginLeft: 6, padding: '1px 5px', borderRadius: 3,
                      background: trade.color + '22', color: trade.color,
                      fontSize: 10, fontWeight: 700,
                    }}>
                      {trade.label}
                    </span>
                  </div>
                </div>
                <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 12 }}>
                  <div className="af-mono" style={{ fontSize: 16, fontWeight: 800, color: t.text }}>
                    {amt.toLocaleString('fr-FR')} €
                  </div>
                  {q.paid > 0 && (
                    <div style={{ fontSize: 10, color: '#10B981', fontWeight: 700 }}>
                      ✓ {q.paid.toLocaleString('fr-FR')} € encaissé
                    </div>
                  )}
                </div>
              </div>
              <div style={{
                display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 8,
              }}>
                <AfBadge status={q.status} t={t} />
                <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                  {q.dueDate && (
                    <span style={{ fontSize: 10, color: t.textDim }}>
                      <lucide.Clock size={10} style={{ verticalAlign: 'middle' }} /> {q.dueDate}
                    </span>
                  )}
                  <span className="af-mono" style={{ fontSize: 10, color: t.textDim }}>{q.id}</span>
                </div>
              </div>
              {(() => {
                const p = (typeof AF_STORE !== 'undefined' && AF_STORE.localSyncPresentation) ? AF_STORE.localSyncPresentation(q) : null;
                if (!p) return null;
                const c = p.state === 'error' ? t.safety : p.state === 'local_only' ? t.textMuted : p.state === 'synced' ? '#10B981' : t.primary;
                return (
                  <div style={{ fontSize: 9, fontWeight: 700, color: c, marginTop: 4, lineHeight: 1.3 }}>
                    {p.shortLabel} — {p.title}
                  </div>
                );
              })()}
              {/* Barre acompte */}
              <div style={{ marginTop: 8 }}>
                <div style={{ height: 3, borderRadius: 2, background: t.bgSunken, overflow: 'hidden' }}>
                  <div style={{
                    height: '100%', borderRadius: 2,
                    background: q.status === 'paye' ? '#10B981' : q.status === 'retard' ? t.safety : t.primary,
                    width: q.status === 'paye' ? '100%' : q.paid > 0 ? '30%' : '0%',
                    transition: 'width 0.5s',
                  }} />
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// LISTE CLIENTS
// ──────────────────────────────────────────────────────────────
function AfClientsList({ t, currency, setView }) {
  const [search, setSearch] = React.useState('');
  const [allDevis, setAllDevis] = React.useState(() => AF_STORE.getAll());
  React.useEffect(() => {
    setAllDevis(AF_STORE.getAll());
    return AF_STORE.subscribe(() => setAllDevis(AF_STORE.getAll()));
  }, []);

  const clientRows = (typeof afGetClientsListForUI === 'function')
    ? afGetClientsListForUI(allDevis)
    : AF_CLIENTS;
  const filtered = clientRows.filter(c =>
    c.name.toLowerCase().includes(search.toLowerCase()) ||
    (c.address || '').toLowerCase().includes(search.toLowerCase())
  );
  const nClients = clientRows.length;

  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflowY: 'auto', background: t.bg }}>
      <AfScreenHeader title="Clients" subtitle={`${nClients} client${nClients === 1 ? '' : 's'}`} t={t} tape />

      {/* Barre de recherche */}
      <div style={{ padding: '10px 16px', borderBottom: `1px solid ${t.border}` }}>
        <div style={{ position: 'relative' }}>
          <lucide.Search size={16} color={t.textMuted}
            style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)' }} />
          <input
            value={search}
            onChange={e => setSearch(e.target.value)}
            placeholder="Rechercher un client…"
            style={{
              width: '100%', padding: '10px 12px 10px 36px', borderRadius: 8,
              background: t.bgElevated, border: `1.5px solid ${t.border}`,
              fontSize: 14, fontWeight: 500, color: t.text, outline: 'none',
            }}
          />
        </div>
      </div>

      {/* Liste */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '12px 16px 24px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {filtered.length === 0 && (
          <div style={{
            background: t.bgElevated,
            borderRadius: 10,
            border: `1px dashed ${t.border}`,
            padding: '22px 16px',
            textAlign: 'center',
          }}>
            <div style={{ fontSize: 14, fontWeight: 800, color: t.text, marginBottom: 6 }}>
              Aucun client enregistré
            </div>
            <div style={{ fontSize: 12, color: t.textMuted, marginBottom: 14, lineHeight: 1.45 }}>
              Les clients sont associés à vos devis. Commencez par une dictée pour créer un devis.
            </div>
            <button
              type="button"
              onClick={() => setView('note-flash')}
              style={{
                padding: '12px 18px',
                borderRadius: 10,
                background: `linear-gradient(135deg, ${t.safety} 0%, #ff8c00 100%)`,
                border: 'none',
                fontSize: 13,
                fontWeight: 800,
                color: '#0D1520',
                cursor: 'pointer',
                boxShadow: `0 4px 14px rgba(255,107,0,0.35)`,
              }}
            >
              Créer un devis avec Instant Note
            </button>
          </div>
        )}
        {filtered.map(c => {
          const trade = AF_TRADE_META[c.trade] || AF_TRADE_META.multi;
          const clientQuotes = allDevis.filter(q => (q.client || q.clientId) === c.id);
          const totalCA = clientQuotes.reduce((s, q) => {
            const od = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(q) : null;
            return s + (od ? od.totalTTC : (q.totalTTC != null ? q.totalTTC : q.amount));
          }, 0);
          return (
            <div key={c.id} style={{
              background: t.bgElevated, borderRadius: 10,
              border: `1px solid ${t.border}`, padding: '14px',
              boxShadow: t.shadowSm,
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                  <div style={{
                    width: 42, height: 42, borderRadius: 10,
                    background: trade.color + '20', border: `1px solid ${trade.color}44`,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    flexShrink: 0,
                  }}>
                    <lucide.User size={20} color={trade.color} />
                  </div>
                  <div>
                    <div style={{ fontSize: 14, fontWeight: 800, color: t.text }}>{c.name}</div>
                    <div style={{ fontSize: 11, color: t.textMuted, marginTop: 2 }}>
                      <lucide.Building2 size={10} style={{ verticalAlign: 'middle', marginRight: 3 }} />
                      {c.address}
                    </div>
                    <div style={{ fontSize: 11, color: t.textMuted, marginTop: 1 }}>
                      <lucide.Phone size={10} style={{ verticalAlign: 'middle', marginRight: 3 }} />
                      {c.phone}
                    </div>
                  </div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <span style={{
                    fontSize: 10, padding: '2px 7px', borderRadius: 4,
                    background: trade.color + '22', color: trade.color,
                    fontWeight: 700, textTransform: 'uppercase',
                  }}>
                    {trade.label}
                  </span>
                  <div className="af-mono" style={{ fontSize: 15, fontWeight: 800, color: t.text, marginTop: 4 }}>
                    {totalCA.toLocaleString('fr-FR')} €
                  </div>
                  <div style={{ fontSize: 10, color: t.textMuted }}>{c.projects} devis</div>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// DÉTAIL DEVIS
// ──────────────────────────────────────────────────────────────
function AfQuoteDetail({ t, currency, quoteId, onBack, setView }) {
  const [q, setQ] = React.useState(() => (quoteId ? AF_STORE.getDevisById(quoteId) : null));
  const [integrityResult, setIntegrityResult] = React.useState(null); // null | {ok, reason}
  const [integrityLoading, setIntegrityLoading] = React.useState(false);
  const [sendSignatureState, setSendSignatureState] = React.useState('idle'); // idle|sending|success|error
  const [sendSignatureMsg, setSendSignatureMsg] = React.useState('');
  const [emailEditOpen, setEmailEditOpen] = React.useState(false);
  const [emailInput, setEmailInput] = React.useState('');
  const [emailSaving, setEmailSaving] = React.useState(false);
  const [emailError, setEmailError] = React.useState('');

  React.useEffect(() => {
    if (!quoteId) { setQ(null); return; }
    const refresh = () => { setQ(AF_STORE.getDevisById(quoteId)); setIntegrityResult(null); };
    refresh();
    return AF_STORE.subscribe(refresh);
  }, [quoteId]);

  const handleVerifyIntegrity = async () => {
    if (!q || typeof AF_DEVIS === 'undefined' || !AF_DEVIS.verifySignedDocumentIntegrity) return;
    setIntegrityLoading(true);
    setIntegrityResult(null);
    try {
      const result = await AF_DEVIS.verifySignedDocumentIntegrity(q);
      setIntegrityResult(result);
    } catch (e) {
      setIntegrityResult({ ok: false, reason: e.message || 'error' });
    } finally {
      setIntegrityLoading(false);
    }
  };

  if (!quoteId || !q) {
    return (
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', background: t.bg, overflow: 'hidden' }}>
        <AfScreenHeader title="Devis introuvable" t={t} back onBack={onBack} subtitle="Référence invalide" />
        <div style={{ padding: 24, color: t.textMuted, fontSize: 14, fontWeight: 600 }}>
          Aucun devis « {String(quoteId)} ». Vérifiez l’identifiant.
        </div>
      </div>
    );
  }

  const client = ((typeof afIsDemoDataEnabled === 'function' && afIsDemoDataEnabled() && typeof AF_CLIENTS !== 'undefined')
    ? AF_CLIENTS.find(c => c.id === (q.client || q.clientId))
    : null) || {
    id: q.client || q.clientId,
    name: q.clientName || '',
    address: '',
    phone: '',
    projects: 0,
    trade: q.trade || 'multi',
  };
  const trade = AF_TRADE_META[q.trade] || AF_TRADE_META.multi;
  const legalState = (typeof AF_STORE !== 'undefined' && AF_STORE.getQuoteLegalState)
    ? AF_STORE.getQuoteLegalState(q)
    : { state: 'unknown', locked: true, requiresNewVersion: true };
  const canEdit = (typeof AF_STORE !== 'undefined' && AF_STORE.canEditQuote)
    ? AF_STORE.canEditQuote(q)
    : false;
  const canSign = (typeof AF_STORE !== 'undefined' && AF_STORE.canSignQuote)
    ? AF_STORE.canSignQuote(q)
    : false;
  const canSend = (typeof AF_STORE !== 'undefined' && AF_STORE.canSendQuote)
    ? AF_STORE.canSendQuote(q)
    : false;
  const signatureRequest = (q && q.signatureRequest && typeof q.signatureRequest === 'object') ? q.signatureRequest : null;
  const hasSignatureRequest = !!(signatureRequest && (signatureRequest.snapshotId || signatureRequest.sentAt || signatureRequest.status === 'sent'));
  const alreadySentForSignature = hasSignatureRequest || legalState.state === 'sent' || legalState.state === 'viewed';
  const shouldShowVersionButton = !canEdit && (
    legalState.requiresNewVersion
    || legalState.state === 'sent'
    || legalState.state === 'viewed'
    || legalState.state === 'signed'
    || legalState.state === 'paid'
    || legalState.state === 'locked'
  );
  const readOnly = !canEdit;
  const currentClientEmail = String(
    (q && q.client && typeof q.client === 'object' ? q.client.email : '')
    || (q && q.clientEmail)
    || (q && q.emailClient)
    || ''
  ).trim();
  const isEmailMissing = !currentClientEmail;
  const canEditClientEmail = canEdit && !readOnly && !alreadySentForSignature;
  const dOut = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(q) : null;
  const docFrozen = !!(dOut && dOut.sealed);

  const refAmt = (q.totalTTC != null ? q.totalTTC : q.amount) || 0;
  const lignes = (dOut && dOut.sealed)
    ? dOut.lignes
    : ((q.lignes && q.lignes.length) ? q.lignes.map((l) => ({
    label: l.label || l.description || 'Ligne',
    qty: l.qty != null ? l.qty : l.quantite,
    unit: l.unit || l.unite || 'u',
    pu: l.pu != null ? l.pu : l.prixUnitaire,
    tva: l.tva != null ? l.tva : 20,
  })) : [
    { label: 'Fourniture matériel', qty: 1, unit: 'forfait', pu: Math.round(refAmt * 0.4), tva: 10 },
    { label: 'Main d\'œuvre', qty: Math.max(1, Math.round(refAmt / 110)), unit: 'h', pu: 55, tva: 20 },
    { label: 'Déplacement & frais', qty: 1, unit: 'forfait', pu: Math.round(refAmt * 0.05), tva: 20 },
  ]);

  const tcalc = (!docFrozen && typeof AF_DEVIS !== 'undefined') ? AF_DEVIS.calcFromLignes(lignes) : null;
  const totalHT  = docFrozen ? dOut.totalHT : (tcalc ? tcalc.totalHT : lignes.reduce((s, l) => s + l.qty * l.pu, 0));
  const totalTVA = docFrozen ? dOut.totalTVA : (tcalc ? tcalc.totalTVA : lignes.reduce((s, l) => s + l.qty * l.pu * (l.tva / 100), 0));
  const fromLinesTTC = tcalc ? tcalc.totalTTC : (totalHT + totalTVA);
  const displayTTC = docFrozen
    ? dOut.totalTTC
    : ((q.lignes && q.lignes.length) ? fromLinesTTC : (Number(q.totalTTC) || Number(q.amount) || fromLinesTTC));
  const _detailAcomptePct = typeof AF_PROFILE_STORE !== 'undefined' ? (AF_PROFILE_STORE.get().acomptePercent ?? 30) : 30;
  const acompte  = docFrozen
    ? dOut.acompte
    : ((q.lignes && q.lignes.length)
      ? (tcalc ? tcalc.acompte : Math.round(displayTTC * (_detailAcomptePct / 100) * 100) / 100)
      : Math.round(displayTTC * (_detailAcomptePct / 100) * 100) / 100);
  const marge = Math.round((docFrozen ? totalHT : (tcalc ? tcalc.totalHT : totalHT)) * 0.35);

  const tvaGroups = lignes.reduce((acc, l) => {
    const rate = l.tva !== undefined ? l.tva : 20;
    const tva = l.qty * l.pu * (rate / 100);
    acc[rate] = (acc[rate] || 0) + tva;
    return acc;
  }, {});

  const syncP = (typeof AF_STORE !== 'undefined' && AF_STORE.localSyncPresentation)
    ? AF_STORE.localSyncPresentation(q) : null;

  const sigLegacy = typeof AF_SIGN_STORE !== 'undefined' ? AF_SIGN_STORE.get(q.id) : null;
  const sig = q.signatureDataUrl
    ? { dataUrl: q.signatureDataUrl, date: q.signedAt ? new Date(q.signedAt).toLocaleDateString('fr-FR') : (sigLegacy && sigLegacy.date) }
    : sigLegacy;
  const hasSig = !!(sig && sig.dataUrl);

  const [photos, setPhotos] = React.useState(q.photos || []);
  const [storeError, setStoreError] = React.useState('');
  const [fiscalTick, setFiscalTick] = React.useState(0);
  const [fiscalCloud, setFiscalCloud] = React.useState({ status: 'idle', data: null, message: '' });

  React.useEffect(() => {
    setPhotos(q.photos || []);
  }, [q.photos, quoteId]);

  React.useEffect(() => {
    setEmailInput(currentClientEmail);
    if (!currentClientEmail) setEmailEditOpen(true);
    if (currentClientEmail) setEmailError('');
  }, [currentClientEmail, quoteId]);

  React.useEffect(() => {
    if (typeof AF_PROFILE_STORE === 'undefined') return undefined;
    return AF_PROFILE_STORE.subscribe(() => setFiscalTick((n) => n + 1));
  }, []);

  const handleAddPhoto = (e) => {
    if (readOnly) return;
    if (typeof AF_STORE !== 'undefined' && q.id && AF_STORE.isDevisReadOnlyById(q.id)) return;
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (event) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        let width = img.width;
        let height = img.height;
        const max = 800;
        if (width > height && width > max) { height *= max / width; width = max; }
        else if (height > max) { width *= max / height; height = max; }
        canvas.width = width; canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);

        // Watermark (Horodatage)
        const now = new Date();
        const dateStr = now.toLocaleDateString('fr-FR');
        const timeStr = now.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
        const text = `${dateStr} à ${timeStr}`;

        const fontSize = Math.max(16, Math.floor(width * 0.04));
        ctx.font = `bold ${fontSize}px sans-serif`;
        ctx.textAlign = 'right';
        ctx.textBaseline = 'bottom';
        
        const padding = 10;
        const x = width - padding;
        const y = height - padding;
        
        const textWidth = ctx.measureText(text).width;
        ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
        ctx.fillRect(x - textWidth - 8, y - fontSize - 8, textWidth + 16, fontSize + 16);

        ctx.lineWidth = 2;
        ctx.strokeStyle = '#000000';
        ctx.strokeText(text, x, y);
        ctx.fillStyle = '#FFFFFF';
        ctx.fillText(text, x, y);

        const dataUrl = canvas.toDataURL('image/jpeg', 0.6);
        const newPhotos = [...photos, { id: Date.now(), data: dataUrl }];
        setPhotos(newPhotos);
        setStoreError('');
        (async () => {
          if (typeof AF_STORE !== 'undefined' && AF_STORE.updateDevis) {
            const res = await AF_STORE.updateDevis(q.id, { photos: newPhotos });
            if (res && res.ok === false && res.userMessage) {
              setStoreError(res.userMessage);
              const fresh = AF_STORE.getDevisById ? AF_STORE.getDevisById(q.id) : null;
              setPhotos(fresh && Array.isArray(fresh.photos) ? fresh.photos : []);
            }
          }
        })();
      };
      img.src = event.target.result;
    };
    reader.readAsDataURL(file);
  };

  const handleRemovePhoto = (id) => {
    if (readOnly) return;
    if (typeof AF_STORE !== 'undefined' && q.id && AF_STORE.isDevisReadOnlyById(q.id)) return;
    const newPhotos = photos.filter(p => p.id !== id);
    setPhotos(newPhotos);
    setStoreError('');
    (async () => {
      if (typeof AF_STORE !== 'undefined' && AF_STORE.updateDevis) {
        const res = await AF_STORE.updateDevis(q.id, { photos: newPhotos });
        if (res && res.ok === false && res.userMessage) {
          setStoreError(res.userMessage);
          const fresh = AF_STORE.getDevisById ? AF_STORE.getDevisById(q.id) : null;
          setPhotos(fresh && Array.isArray(fresh.photos) ? fresh.photos : []);
        }
      }
    })();
  };

  const fiscalDiag = React.useMemo(() => {
    if (typeof AF_FISCAL_ENGINE === 'undefined' || !q) return null;
    try {
      return AF_FISCAL_ENGINE.analyzeDevis(q);
    } catch (e) {
      return null;
    }
  }, [q, fiscalTick]);

  const shouldCallFiscalCloud = React.useMemo(() => {
    if (!fiscalDiag) return false;
    const d = fiscalDiag;
    const hasBlocking = []
      .concat(d.issues || [], d.warnings || [], d.info || [])
      .some((x) => x && x.blocking);
    if (hasBlocking) return true;
    if (d.level === 'error') return true;
    if (d.confidence === 'low' || d.confidence === 'medium') return true;
    return false;
  }, [fiscalDiag]);

  React.useEffect(() => {
    if (!q || !shouldCallFiscalCloud) {
      setFiscalCloud({ status: 'idle', data: null, message: '' });
      return undefined;
    }
    if (typeof navigator !== 'undefined' && !navigator.onLine) {
      setFiscalCloud({
        status: 'error',
        data: null,
        message: "Hors ligne : l'aide explicative n'est pas joignable. Le diagnostic local ci-dessus reste la référence.",
      });
      return undefined;
    }
    if (typeof AF_WORKER === 'undefined' || !AF_WORKER.fiscalExplain || typeof AF_FISCAL_ENGINE === 'undefined' || !AF_FISCAL_ENGINE.buildFiscalAgentPayload) {
      setFiscalCloud({
        status: 'error',
        data: null,
        message: "Modules Worker ou moteur fiscal indisponibles : complément non chargé.",
      });
      return undefined;
    }
    let cancelled = false;
    setFiscalCloud({ status: 'loading', data: null, message: '' });
    (async () => {
      const payload = AF_FISCAL_ENGINE.buildFiscalAgentPayload(q);
      const res = await AF_WORKER.fiscalExplain(payload);
      if (cancelled) return;
      if (res && res.ok && res.data) {
        setFiscalCloud({ status: 'ok', data: res.data, message: '' });
        return;
      }
      if (res && res.skipped && res.reason === 'no_agent_base') {
        setFiscalCloud({
          status: 'skipped',
          data: null,
          message: "Aucune base Worker (AF_INTEGRATION.workersBase, ou adresse d'agent) : l'aide distante n'est pas appelée. Le contrôle local reste affiché.",
        });
        return;
      }
      setFiscalCloud({
        status: 'error',
        data: null,
        message: (res && res.userMessage) ? res.userMessage : "L'aide explicative est indisponible. Conservez le diagnostic local.",
      });
    })();
    return () => { cancelled = true; };
  }, [q, shouldCallFiscalCloud, quoteId, fiscalTick]);

  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflowY: 'auto', background: t.bg }}>
      <AfScreenHeader
        title={q.id}
        subtitle={q.clientName}
        t={t} back onBack={onBack}
        tape={q.status === 'retard'}
        rightAction={
          <div style={{ display: 'flex', gap: 8 }}>
            <button
              onClick={() => typeof AF_PDF !== 'undefined' && AF_PDF.download({
                ...q,
                lignes: lignes,
                totalHT, totalTVA, totalTTC: displayTTC, acompte,
              }, 'Artisan')
              }
              title="Télécharger PDF"
              style={{
                width: 40, height: 40, borderRadius: 8,
                background: t.bgElevated, border: `1px solid ${t.border}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
              }}>
              <lucide.Download size={18} color={t.primary} />
            </button>
          </div>
        }
      />

      <div style={{ flex: 1, overflowY: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: 12 }}>
        {syncP && (
          <div style={{
            padding: '10px 12px', borderRadius: 10, fontSize: 12, fontWeight: 600, lineHeight: 1.45,
            background: syncP.state === 'error' ? 'rgba(185, 28, 28, 0.1)' : syncP.state === 'local_only' ? 'rgba(11, 61, 92, 0.08)' : syncP.state === 'synced' ? 'rgba(16, 185, 129, 0.1)' : 'rgba(234, 179, 8, 0.12)',
            border: '1px solid ' + (syncP.state === 'error' ? 'rgba(185, 28, 28, 0.35)' : syncP.state === 'local_only' ? 'rgba(11, 61, 92, 0.2)' : syncP.state === 'synced' ? 'rgba(16, 185, 129, 0.35)' : 'rgba(180, 83, 9, 0.3)'),
            color: syncP.state === 'error' ? '#B91C1C' : t.text,
          }}>
            <div style={{ fontWeight: 800, marginBottom: 4, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.5, opacity: 0.9 }}>
              Synchronisation · {syncP.shortLabel}
            </div>
            <div style={{ color: t.text, fontSize: 12 }}>{syncP.title}{syncP.detail ? ' — ' + syncP.detail : ''}</div>
          </div>
        )}
        {fiscalDiag && (
          <div style={{
            background: t.bgElevated, borderRadius: 10,
            border: '1px solid ' + (fiscalDiag.level === 'error' ? 'rgba(185, 28, 28, 0.45)' : fiscalDiag.level === 'warning' ? 'rgba(180, 83, 9, 0.45)' : 'rgba(16, 185, 129, 0.35)'),
            padding: '12px 14px',
          }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: 0.5, color: t.textMuted, textTransform: 'uppercase', marginBottom: 2 }}>
              Diagnostic fiscal (V2)
            </div>
            {fiscalDiag.meta && (fiscalDiag.meta.rulesetVersion != null || fiscalDiag.meta.engineVersion != null) && (
              <div style={{ fontSize: 9, color: t.textDim, marginBottom: 6 }}>
                Ruleset {fiscalDiag.meta.rulesetVersion != null ? fiscalDiag.meta.rulesetVersion : '—'}
                {fiscalDiag.meta.engineVersion != null ? ' · moteur ' + fiscalDiag.meta.engineVersion : ''}
              </div>
            )}
            <div style={{ fontSize: 12, fontWeight: 700, color: t.text, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 6 }}>
              {fiscalDiag.level === 'ok' && <lucide.CheckCircle size={16} color="#10B981" />}
              {fiscalDiag.level === 'warning' && <lucide.AlertTriangle size={16} color="#D97706" />}
              {fiscalDiag.level === 'error' && <lucide.AlertTriangle size={16} color="#B91C1C" />}
              <span>{fiscalDiag.ok ? 'Contrôle' : 'Points à traiter'} — confiance {fiscalDiag.confidence}</span>
            </div>
            <p style={{ fontSize: 12, lineHeight: 1.5, color: t.text, margin: '0 0 8px' }}>{fiscalDiag.summary}</p>
            {fiscalDiag.issues && fiscalDiag.issues.length > 0 && (
              <ul style={{ margin: '4px 0 0 18px', padding: 0, fontSize: 12, color: '#B91C1C' }}>
                {fiscalDiag.issues.map((it, i) => (
                  <li key={it.code + '-' + i} style={{ marginBottom: 4 }}>
                    {it.category && <span style={{ fontSize: 9, fontWeight: 800, textTransform: 'uppercase', color: t.textDim, display: 'block' }}>{it.category}{it.blocking ? ' · bloquant' : ''}</span>}
                    {it.message}
                  </li>
                ))}
              </ul>
            )}
            {fiscalDiag.warnings && fiscalDiag.warnings.length > 0 && (
              <ul style={{ margin: '4px 0 0 18px', padding: 0, fontSize: 12, color: '#B45309' }}>
                {fiscalDiag.warnings.map((it, i) => (
                  <li key={it.code + '-' + i} style={{ marginBottom: 4 }}>
                    {it.category && <span style={{ fontSize: 9, fontWeight: 800, textTransform: 'uppercase', color: t.textDim, display: 'block' }}>{it.category}</span>}
                    {it.message}
                  </li>
                ))}
              </ul>
            )}
            {fiscalDiag.missing && fiscalDiag.missing.length > 0 && (
              <div style={{ fontSize: 10, color: t.textMuted, marginTop: 6 }}>
                Champs / points : {fiscalDiag.missing.join(' · ')}
              </div>
            )}
            {fiscalDiag.info && fiscalDiag.info.length > 0 && (
              <ul style={{ margin: '4px 0 0 18px', padding: 0, fontSize: 12, color: t.primary, listStyle: 'disc' }}>
                {fiscalDiag.info.map((it, i) => (
                  <li key={it.code + '-' + i} style={{ marginBottom: 4 }}>
                    {it.category && <span style={{ fontSize: 9, fontWeight: 800, textTransform: 'uppercase', color: t.textDim, display: 'block' }}>{it.category}</span>}
                    {it.message}
                  </li>
                ))}
              </ul>
            )}
            {fiscalDiag.meta && fiscalDiag.meta.disclaimer && (
              <div style={{ fontSize: 9, color: t.textDim, lineHeight: 1.4, marginTop: 8, borderTop: `1px solid ${t.border}`, paddingTop: 8 }}>
                {fiscalDiag.meta.disclaimer}
              </div>
            )}
          </div>
        )}
        {fiscalDiag && shouldCallFiscalCloud && fiscalCloud.status !== 'idle' && (
          <div style={{
            background: t.bgElevated, borderRadius: 10,
            border: '1px solid ' + t.border, padding: '12px 14px',
            marginTop: 2,
          }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: 0.5, color: t.textMuted, textTransform: 'uppercase', marginBottom: 4 }}>
              Aide à la lecture (complément cloud)
            </div>
            <div style={{ fontSize: 9, color: t.textDim, marginBottom: 8 }}>
              Le contrôle des points ci-dessus reste celui du moteur local. Ce complément n’y substitue pas. Pas d’avis juridique.
            </div>
            {fiscalCloud.status === 'loading' && (
              <div style={{ fontSize: 12, color: t.textMuted, fontStyle: 'italic' }}>Préparation de l’aide pédagogique…</div>
            )}
            {(fiscalCloud.status === 'error' || fiscalCloud.status === 'skipped') && fiscalCloud.message && (
              <div style={{ fontSize: 12, color: t.textMuted, lineHeight: 1.45 }}>{fiscalCloud.message}</div>
            )}
            {fiscalCloud.status === 'ok' && fiscalCloud.data && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {fiscalCloud.data.source && (
                  <div style={{ fontSize: 9, color: t.textDim, fontWeight: 700, textTransform: 'uppercase' }}>
                    {fiscalCloud.data.source === 'openai' ? 'Mode enrichi (indicatif)' : 'Repères pédagogiques (serveur)'}
                  </div>
                )}
                {fiscalCloud.data.synthesis && (
                  <p style={{ fontSize: 12, lineHeight: 1.5, color: t.text, margin: 0 }}>{fiscalCloud.data.synthesis}</p>
                )}
                {fiscalCloud.data.reformulation && (
                  <p style={{ fontSize: 12, lineHeight: 1.5, color: t.text, margin: 0, fontWeight: 600 }}>À retenir : {fiscalCloud.data.reformulation}</p>
                )}
                {fiscalCloud.data.concreteActions && fiscalCloud.data.concreteActions.length > 0 && (
                  <div>
                    <div style={{ fontSize: 10, fontWeight: 800, textTransform: 'uppercase', color: t.textMuted, marginBottom: 4 }}>Pistes d’action</div>
                    <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12, lineHeight: 1.5, color: t.text }}>
                      {fiscalCloud.data.concreteActions.map((a, i) => <li key={i} style={{ marginBottom: 4 }}>{a}</li>)}
                    </ul>
                  </div>
                )}
                {fiscalCloud.data.issueExplanations && fiscalCloud.data.issueExplanations.length > 0 && (
                  <div>
                    <div style={{ fontSize: 10, fontWeight: 800, textTransform: 'uppercase', color: t.textMuted, marginBottom: 6 }}>Détail des points (lecture ciblée)</div>
                    {fiscalCloud.data.issueExplanations.map((ex, i) => (
                      <div key={i} style={{ marginBottom: 10, paddingBottom: 8, borderBottom: i < fiscalCloud.data.issueExplanations.length - 1 ? `1px solid ${t.border}` : 'none' }}>
                        {ex.explanation && <div style={{ fontSize: 12, color: t.text, lineHeight: 1.45 }}>{ex.explanation}</div>}
                        {ex.suggestedActions && ex.suggestedActions.length > 0 && (
                          <ul style={{ margin: '4px 0 0 16px', padding: 0, fontSize: 11, color: t.textMuted }}>
                            {ex.suggestedActions.map((sa, j) => <li key={j} style={{ marginBottom: 2 }}>{sa}</li>)}
                          </ul>
                        )}
                      </div>
                    ))}
                  </div>
                )}
                {fiscalCloud.data.disclaimer && (
                  <div style={{ fontSize: 9, color: t.textDim, lineHeight: 1.4, borderTop: `1px solid ${t.border}`, paddingTop: 8, marginTop: 2 }}>
                    {fiscalCloud.data.disclaimer}
                  </div>
                )}
              </div>
            )}
          </div>
        )}
        {/* Statut & infos client */}
        <div style={{
          background: t.bgElevated, borderRadius: 10,
          border: `1px solid ${t.border}`, padding: '14px',
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
            <AfBadge status={q.status} t={t} size="lg" />
            <span style={{
              fontSize: 10, padding: '3px 8px', borderRadius: 4,
              background: trade.color + '22', color: trade.color, fontWeight: 700,
            }}>
              {trade.label}
            </span>
          </div>
          <div style={{ fontSize: 15, fontWeight: 800, color: t.text, marginBottom: 6 }}>{q.title}</div>
          <div style={{ fontSize: 12, color: t.textMuted }}>
            <lucide.User size={11} style={{ verticalAlign: 'middle', marginRight: 4 }} />
            {client.name || q.clientName} · {client.address || '—'}
          </div>
          <div style={{ fontSize: 12, color: t.textMuted, marginTop: 3 }}>
            <lucide.Phone size={11} style={{ verticalAlign: 'middle', marginRight: 4 }} />
            {client.phone || '—'}
          </div>
          <div style={{ fontSize: 12, color: t.textMuted, marginTop: 3 }}>
            <lucide.User size={11} style={{ verticalAlign: 'middle', marginRight: 4 }} />
            {currentClientEmail || 'Email non renseigné'}
          </div>
          <div style={{
            display: 'flex', gap: 12, marginTop: 10,
            paddingTop: 10, borderTop: `1px solid ${t.border}`,
            fontSize: 11, color: t.textMuted,
          }}>
            <span>Créé le {q.createdAt}</span>
            {q.dueDate && <span>Échéance {q.dueDate}</span>}
          </div>
        </div>

        {(isEmailMissing || emailEditOpen || emailError) && (
          <div style={{
            background: isEmailMissing ? 'rgba(185,28,28,0.08)' : t.bgElevated,
            borderRadius: 10,
            border: isEmailMissing ? '1px solid rgba(185,28,28,0.35)' : `1px solid ${t.border}`,
            padding: '12px 14px',
            display: 'flex',
            flexDirection: 'column',
            gap: 10,
          }}>
            <div style={{ fontSize: 13, fontWeight: 800, color: t.text }}>Email du client requis</div>
            <div style={{ fontSize: 12, color: t.textMuted, lineHeight: 1.45 }}>
              Ajoutez l’email du client pour envoyer le lien de signature.
            </div>
            <input
              value={emailInput}
              onChange={(e) => { setEmailInput(e.target.value); setEmailError(''); }}
              placeholder="email@client.fr"
              disabled={!canEditClientEmail || emailSaving}
              style={{
                width: '100%',
                height: 46,
                borderRadius: 8,
                border: `1.5px solid ${emailError ? '#B91C1C' : t.border}`,
                background: (!canEditClientEmail || emailSaving) ? t.bgSunken : t.bgElevated,
                color: t.text,
                fontSize: 14,
                padding: '0 12px',
              }}
            />
            {emailError ? (
              <div style={{ fontSize: 12, color: '#B91C1C', fontWeight: 600 }}>{emailError}</div>
            ) : null}
            {canEditClientEmail ? (
              <button
                type="button"
                onClick={async () => {
                  const normalized = String(emailInput || '').trim().toLowerCase();
                  if (!normalized || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized)) {
                    setEmailError('Entrez un email client valide.');
                    return;
                  }
                  setEmailSaving(true);
                  setEmailError('');
                  const patch = {
                    clientEmail: normalized,
                    emailClient: normalized,
                    _auditEvent: {
                      type: 'client_email_updated',
                      actor: 'artisan',
                      label: 'Email client mis à jour',
                      metadata: { source: 'quote_detail_signature_flow' },
                    },
                  };
                  if (q && q.client && typeof q.client === 'object') {
                    patch.client = { ...q.client, email: normalized };
                  }
                  const res = await AF_STORE.updateDevis(q.id, patch);
                  setEmailSaving(false);
                  if (!res || res.ok === false) {
                    setEmailError((res && res.userMessage) ? String(res.userMessage) : 'Impossible d’enregistrer cet email.');
                    return;
                  }
                  setEmailEditOpen(false);
                  setSendSignatureMsg('');
                }}
                disabled={emailSaving}
                style={{
                  width: '100%',
                  height: 46,
                  borderRadius: 10,
                  border: 'none',
                  background: t.primary,
                  color: '#fff',
                  fontSize: 14,
                  fontWeight: 800,
                  cursor: emailSaving ? 'not-allowed' : 'pointer',
                  opacity: emailSaving ? 0.7 : 1,
                }}
              >
                {emailSaving ? 'Enregistrement…' : 'Enregistrer l’email'}
              </button>
            ) : (
              <div style={{ fontSize: 11, color: t.textMuted, lineHeight: 1.45 }}>
                Email en lecture seule : ce devis ne peut plus être modifié directement.
              </div>
            )}
          </div>
        )}

        {currentClientEmail && canEditClientEmail && !emailEditOpen && (
          <button
            type="button"
            onClick={() => setEmailEditOpen(true)}
            style={{
              width: '100%',
              height: 40,
              borderRadius: 8,
              border: `1px solid ${t.border}`,
              background: t.bgElevated,
              color: t.text,
              fontSize: 12,
              fontWeight: 700,
            }}
          >
            Modifier l’email
          </button>
        )}

        {/* Lignes devis */}
        <div style={{
          background: t.bgElevated, borderRadius: 10,
          border: `1px solid ${t.border}`, overflow: 'hidden',
        }}>
          <div style={{
            padding: '10px 14px',
            borderBottom: `1px solid ${t.border}`,
            fontSize: 11, fontWeight: 700, color: t.textMuted,
            textTransform: 'uppercase', letterSpacing: 0.6,
            background: t.bgSunken,
          }}>
            Lignes du devis ({lignes.length})
          </div>
          {lignes.map((l, i) => (
            <div key={i} style={{
              padding: '11px 14px',
              borderBottom: i < lignes.length - 1 ? `1px solid ${t.border}` : 'none',
              display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            }}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, color: t.text }}>{l.label}</div>
                <div style={{ fontSize: 11, color: t.textMuted, marginTop: 2 }}>
                  {l.qty} {l.unit} × {l.pu} € HT
                  <span style={{
                    marginLeft: 6, fontSize: 10, padding: '1px 5px', borderRadius: 3,
                    background: `rgba(11,61,92,0.1)`, color: t.primary, fontWeight: 700,
                  }}>
                    TVA {l.tva}%
                  </span>
                </div>
              </div>
              <div className="af-mono" style={{ fontSize: 14, fontWeight: 800, color: t.text }}>
                {(l.qty * l.pu).toLocaleString('fr-FR')} €
              </div>
            </div>
          ))}
        </div>

        {readOnly && (
          <div style={{ padding: '10px 12px', borderRadius: 8, background: 'rgba(11,61,92,0.08)', border: '1px solid rgba(11,61,92,0.25)', fontSize: 12, fontWeight: 700, color: t.text, lineHeight: 1.45 }}>
            Ce devis est verrouillé pour garantir son intégrité. Pour le modifier, créez une nouvelle version.
          </div>
        )}

        {shouldShowVersionButton && (
          <button
            type="button"
            onClick={async () => {
              if (!AF_STORE || !AF_STORE.createQuoteVersion) return;
              const res = await AF_STORE.createQuoteVersion(q);
              if (res && res.ok && res.quote && res.quote.id) {
                setView('detail', res.quote.id);
              }
            }}
            style={{
              width: '100%',
              height: 46,
              borderRadius: 10,
              border: `1.5px solid ${t.primary}`,
              background: t.bgElevated,
              color: t.primary,
              fontSize: 13,
              fontWeight: 800,
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              gap: 8,
            }}
          >
            <lucide.CopyPlus size={16} />
            Créer une nouvelle version
          </button>
        )}

        {(hasSignatureRequest || sendSignatureState === 'success') && (
          <div style={{ padding: '10px 12px', borderRadius: 8, background: 'rgba(11,61,92,0.08)', border: '1px solid rgba(11,61,92,0.25)', fontSize: 12, color: t.text, lineHeight: 1.45 }}>
            <div style={{ fontWeight: 800, marginBottom: 6 }}>Signature client</div>
            <div>Statut : Déjà envoyé pour signature</div>
            {signatureRequest && signatureRequest.sentAt ? <div>Envoyé le : {String(signatureRequest.sentAt)}</div> : null}
            {signatureRequest && signatureRequest.expiresAt ? <div>Expire le : {String(signatureRequest.expiresAt)}</div> : null}
            <div>
              Réf. d’intégrité : {String((signatureRequest && signatureRequest.quoteHash) || (q.legalSnapshot && q.legalSnapshot.quoteHash) || '—').slice(0, 12)}
            </div>
          </div>
        )}

        {sendSignatureState === 'error' && sendSignatureMsg && (
          <div style={{ padding: '9px 10px', borderRadius: 8, background: 'rgba(185,28,28,0.08)', border: '1px solid rgba(185,28,28,0.35)', fontSize: 12, fontWeight: 600, color: '#B91C1C', lineHeight: 1.45 }}>
            {sendSignatureMsg}
          </div>
        )}
        {sendSignatureState === 'success' && sendSignatureMsg && (
          <div style={{ padding: '9px 10px', borderRadius: 8, background: 'rgba(16,185,129,0.1)', border: '1px solid rgba(16,185,129,0.35)', fontSize: 12, fontWeight: 600, color: '#0F766E', lineHeight: 1.45 }}>
            {sendSignatureMsg}
          </div>
        )}

        {readOnly && (
          <div style={{ padding: '9px 10px', borderRadius: 8, background: 'rgba(234,179,8,0.08)', border: '1px solid rgba(180,83,9,0.25)', fontSize: 11, fontWeight: 600, color: t.text, lineHeight: 1.4 }}>
            Édition bloquée : lignes, TVA, client, conditions, totaux et sauvegarde.
          </div>
        )}

        {docFrozen && q.signedDocumentHash && (
          <div style={{
            padding: '10px 12px', borderRadius: 8, background: t.bgElevated, border: `1px solid ${t.border}`,
            fontSize: 11, color: t.textMuted, fontFamily: 'ui-monospace, monospace', wordBreak: 'break-all',
          }}>
            <div style={{ fontWeight: 800, color: t.text, marginBottom: 4, fontFamily: 'inherit' }}>Intégrité documentaire</div>
            Données figées (HT / TVA / TTC / acompte / lignes) identiques au snapshot signé. Empreinte SHA-256 (charge utile canonique) : {String(q.signedDocumentHash)}
            {q.signedSnapshot && Number(q.signedSnapshot.version) === 2 && q.signedSnapshot.signedAt && (
              <div style={{ marginTop: 8, lineHeight: 1.45, fontFamily: 'inherit' }}>
                Modèle de preuve v2 — instantané figé le {new Date(q.signedSnapshot.signedAt).toLocaleString('fr-FR')}.
                {q.signedSnapshot.signatory && q.signedSnapshot.signatory.name
                  ? ` Signataire enregistré : ${q.signedSnapshot.signatory.name}.`
                  : ''}
                {q.signedSnapshot.legal && q.signedSnapshot.legal.artisan && (q.signedSnapshot.legal.artisan.entreprise || q.signedSnapshot.legal.artisan.nom) ? (
                  <span> Cadre artisan : {String(q.signedSnapshot.legal.artisan.entreprise || q.signedSnapshot.legal.artisan.nom || '')}.</span>
                ) : null}
              </div>
            )}

            {/* Bouton de vérification d'intégrité */}
            <button
              onClick={handleVerifyIntegrity}
              disabled={integrityLoading}
              aria-label="Vérifier l'intégrité cryptographique du document signé"
              style={{
                marginTop: 10, width: '100%', padding: '8px', borderRadius: 6,
                background: integrityLoading ? t.bgSunken : t.primary + '15',
                border: `1px solid ${t.primary}55`,
                fontSize: 11, fontWeight: 700, color: t.primary, cursor: integrityLoading ? 'not-allowed' : 'pointer',
                fontFamily: 'inherit', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
              }}
            >
              <lucide.Lock size={12} />
              {integrityLoading ? 'Vérification en cours…' : 'Vérifier l\'intégrité (SHA-256)'}
            </button>

            {integrityResult && (
              <div style={{
                marginTop: 8, padding: '8px 10px', borderRadius: 6,
                background: integrityResult.ok ? 'rgba(16,185,129,0.1)' : 'rgba(185,28,28,0.08)',
                border: `1px solid ${integrityResult.ok ? '#10B981' : '#B91C1C'}`,
                fontSize: 11, fontWeight: 700, fontFamily: 'inherit',
                color: integrityResult.ok ? '#10B981' : '#B91C1C',
                display: 'flex', alignItems: 'center', gap: 6,
              }}>
                {integrityResult.ok
                  ? <><lucide.CheckCircle size={13} /> Intégrité vérifiée — empreinte conforme au snapshot signé.</>
                  : <><lucide.AlertTriangle size={13} /> Vérification échouée : {integrityResult.reason || 'erreur inconnue'}.</>
                }
              </div>
            )}
          </div>
        )}

        {/* Photos du chantier */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, background: t.bgElevated, borderRadius: 10, padding: '12px 14px', border: `1px solid ${t.border}` }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div style={{ fontSize: 13, fontWeight: 700, color: t.text }}>Photos du chantier</div>
            {canEdit && (
            <label style={{
              background: t.primary + '20', color: t.primary, padding: '4px 10px', borderRadius: 6,
              fontSize: 11, fontWeight: 700, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 4
            }}>
              <lucide.Camera size={14} /> Ajouter
              <input type="file" accept="image/*" capture="environment" style={{ display: 'none' }} onChange={handleAddPhoto} />
            </label>
            )}
          </div>
          {storeError && canEdit && (
            <div style={{
              padding: '8px 10px', borderRadius: 8, fontSize: 11, lineHeight: 1.4,
              background: 'rgba(185,28,28,0.08)', border: '1px solid rgba(185,28,28,0.35)', color: '#B91C1C', fontWeight: 600,
            }}>
              {storeError}
            </div>
          )}
          {photos.length > 0 && (
            <div style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 }}>
              {photos.map(p => (
                <div key={p.id} style={{ position: 'relative', width: 64, height: 64, flexShrink: 0, borderRadius: 8, overflow: 'hidden', border: `1px solid ${t.border}` }}>
                  <img src={p.data} alt="Photo" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                  {canEdit && (
                  <button type="button" onClick={() => handleRemovePhoto(p.id)} style={{
                    position: 'absolute', top: 2, right: 2, background: 'rgba(0,0,0,0.6)', color: '#fff', border: 'none',
                    width: 20, height: 20, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer'
                  }}>
                    <lucide.Trash2 size={12} />
                  </button>
                  )}
                </div>
              ))}
            </div>
          )}
        </div>

        {/* Totaux */}
        <div style={{
          background: t.primary, borderRadius: 10,
          padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 6,
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', color: 'rgba(255,255,255,0.7)', fontSize: 12 }}>
            <span>Total HT</span>
            <span className="af-mono">{totalHT.toLocaleString('fr-FR')} €</span>
          </div>
          {Object.entries(tvaGroups).sort(([a], [b]) => Number(a) - Number(b)).map(([rate, amt]) => amt > 0 && (
            <div key={rate} style={{ display: 'flex', justifyContent: 'space-between', color: 'rgba(255,255,255,0.7)', fontSize: 12 }}>
              <span>Total TVA {rate}%</span>
              <span className="af-mono">{amt.toFixed(2)} €</span>
            </div>
          ))}
          <div style={{ height: 1, background: 'rgba(255,255,255,0.2)', margin: '3px 0' }} />
          <div style={{ display: 'flex', justifyContent: 'space-between', color: '#fff', fontSize: 17, fontWeight: 800 }}>
            <span>Total TTC</span>
            <span className="af-mono">{displayTTC.toLocaleString('fr-FR')} €</span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', color: t.safety, fontSize: 13, fontWeight: 700 }}>
            <span>Acompte {_detailAcomptePct}%</span>
            <span className="af-mono">{acompte.toLocaleString('fr-FR')} €</span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', color: '#34D399', fontSize: 12, fontWeight: 700 }}>
            <span>Marge brute estimée</span>
            <span className="af-mono">+{marge.toLocaleString('fr-FR')} €</span>
          </div>
        </div>

        {/* Boutons Signature et Envoi */}
        <div style={{ display: 'flex', gap: 8 }}>
          <button
            onClick={() => canSign && setView('signature', q.id)}
            disabled={!canSign}
            style={{
              flex: 1, height: 52, borderRadius: 10,
              background: (hasSig || !canSign) ? '#10B981' : t.bgElevated,
              border: `1.5px solid ${(hasSig || !canSign) ? '#10B981' : t.border}`,
              color: (hasSig || !canSign) ? '#fff' : t.text,
              fontSize: 14, fontWeight: 800, cursor: !canSign ? 'not-allowed' : 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
              transition: 'background 0.2s', opacity: !canSign && !hasSig ? 0.6 : 1,
            }}
          >
            {(hasSig || !canSign) ? <lucide.CheckCircle2 size={18} /> : <lucide.PenTool size={18} />}
            {!canSign ? 'Verrouillé' : (hasSig ? 'Signé' : 'Faire signer')}
          </button>

          <div style={{ flex: 2 }}>
            <button
              type="button"
              onClick={async () => {
                if (sendSignatureState === 'sending') return;
                if (!AF_STORE || !AF_STORE.sendQuoteForSignature) {
                  setSendSignatureState('error');
                  setSendSignatureMsg('Le service de signature est temporairement indisponible.');
                  return;
                }
                setSendSignatureState('sending');
                setSendSignatureMsg('');
                const res = await AF_STORE.sendQuoteForSignature({
                  ...q,
                  lignes,
                  totalHT,
                  totalTVA,
                  totalTTC: displayTTC,
                  currency,
                });
                if (res && res.ok) {
                  setSendSignatureState('success');
                  setSendSignatureMsg('Le devis a été envoyé au client pour signature.');
                  return;
                }
                setSendSignatureState('error');
                setSendSignatureMsg((res && res.userMessage) ? String(res.userMessage) : 'Impossible d’envoyer le devis pour signature. Réessayez dans quelques instants.');
              }}
              disabled={!canSend || isEmailMissing || alreadySentForSignature || sendSignatureState === 'sending'}
              style={{
                width: '100%',
                height: 52,
                borderRadius: 10,
                border: `1.5px solid ${(!canSend || isEmailMissing || alreadySentForSignature) ? t.border : t.safety}`,
                background: sendSignatureState === 'sending'
                  ? 'linear-gradient(135deg,#d97706 0%,#f59e0b 100%)'
                  : ((!canSend || isEmailMissing || alreadySentForSignature) ? t.bgElevated : `linear-gradient(135deg, ${t.safety} 0%, #ff8c00 100%)`),
                color: (!canSend || isEmailMissing || alreadySentForSignature) ? t.textMuted : '#0D1520',
                fontSize: 14,
                fontWeight: 800,
                cursor: (!canSend || isEmailMissing || alreadySentForSignature || sendSignatureState === 'sending') ? 'not-allowed' : 'pointer',
                opacity: ((!canSend || isEmailMissing) && !alreadySentForSignature) ? 0.65 : 1,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                gap: 8,
              }}
            >
              {sendSignatureState === 'sending' ? <lucide.Loader size={16} /> : <lucide.Share2 size={16} />}
              {sendSignatureState === 'sending'
                ? 'Envoi en cours…'
                : (alreadySentForSignature ? 'Déjà envoyé pour signature' : 'Envoyer pour signature')}
            </button>
            {isEmailMissing && (
              <div style={{ marginTop: 6, fontSize: 11, fontWeight: 600, color: '#B91C1C', lineHeight: 1.4 }}>
                Ajoutez l’email du client pour envoyer le devis à signer.
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// PARAMÈTRES
// ──────────────────────────────────────────────────────────────
function AfSettings({ t, userName, setUserName, dark, setDark, currency, setCurrency, offline, setOffline, onBack }) {
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflowY: 'auto', background: t.bg }}>
      <AfScreenHeader title="Paramètres" t={t} back onBack={onBack} />

      <div style={{ flex: 1, overflowY: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {/* Section Profil */}
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 1.2, color: t.textMuted, textTransform: 'uppercase', padding: '4px 0 8px', marginTop: 4 }}>
          Profil artisan
        </div>
        <div style={{ background: t.bgElevated, borderRadius: 10, border: `1px solid ${t.border}`, padding: '14px', display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: t.textMuted, display: 'block', marginBottom: 6 }}>Nom artisan</label>
            <input
              value={userName}
              onChange={e => setUserName(e.target.value)}
              style={{
                width: '100%', padding: '11px 12px', borderRadius: 8,
                background: t.bgSunken, border: `1.5px solid ${t.border}`,
                fontSize: 15, fontWeight: 600, color: t.text, outline: 'none',
              }}
            />
          </div>
        </div>

        {/* Section Affichage */}
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 1.2, color: t.textMuted, textTransform: 'uppercase', padding: '12px 0 8px' }}>
          Affichage
        </div>
        <div style={{ background: t.bgElevated, borderRadius: 10, border: `1px solid ${t.border}`, overflow: 'hidden' }}>
          {[
            {
              label: 'Mode sombre', sub: 'Optimisé pour usage en extérieur',
              right: <div onClick={() => setDark(!dark)} style={{
                width: 42, height: 24, borderRadius: 12, cursor: 'pointer',
                background: dark ? t.primary : t.bgSunken,
                position: 'relative', transition: 'background 0.2s',
                border: `1px solid ${dark ? t.primary : t.border}`, flexShrink: 0,
              }}>
                <div style={{
                  position: 'absolute', top: 2, left: dark ? 18 : 2,
                  width: 18, height: 18, borderRadius: '50%', background: '#fff',
                  transition: 'left 0.2s cubic-bezier(0.2, 0.8, 0.2, 1)',
                  boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
                }} />
              </div>,
            },
            {
              label: 'Devise',
              right: <div style={{ display: 'flex', gap: 4 }}>
                {['EUR', 'CHF'].map(c => (
                  <button key={c} onClick={() => setCurrency(c)} style={{
                    padding: '5px 10px', borderRadius: 6, fontSize: 12, fontWeight: 700, cursor: 'pointer',
                    background: currency === c ? t.primary : t.bgSunken,
                    color: currency === c ? '#fff' : t.text,
                    border: `1px solid ${currency === c ? t.primary : t.border}`,
                  }}>{c}</button>
                ))}
              </div>,
            },
          ].map((row, i, arr) => (
            <div key={i} style={{
              display: 'flex', justifyContent: 'space-between', alignItems: 'center',
              padding: '14px 16px',
              borderBottom: i < arr.length - 1 ? `1px solid ${t.border}` : 'none',
            }}>
              <div>
                <div style={{ fontSize: 14, fontWeight: 600, color: t.text }}>{row.label}</div>
                {row.sub && <div style={{ fontSize: 11, color: t.textMuted, marginTop: 1 }}>{row.sub}</div>}
              </div>
              {row.right}
            </div>
          ))}
        </div>

        {/* Section Réseau */}
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 1.2, color: t.textMuted, textTransform: 'uppercase', padding: '12px 0 8px' }}>
          Réseau & synchro
        </div>
        <div style={{ background: t.bgElevated, borderRadius: 10, border: `1px solid ${t.border}`, padding: '14px 16px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div>
              <div style={{ fontSize: 14, fontWeight: 600, color: t.text }}>Mode hors-ligne</div>
              <div style={{ fontSize: 11, color: t.textMuted, marginTop: 1 }}>Firestore sync · Usage chantier sans réseau</div>
            </div>
            <div onClick={() => setOffline(!offline)} style={{
              width: 42, height: 24, borderRadius: 12, cursor: 'pointer',
              background: offline ? t.safety : t.bgSunken,
              position: 'relative', transition: 'background 0.2s',
              border: `1px solid ${offline ? t.safety : t.border}`, flexShrink: 0,
            }}>
              <div style={{
                position: 'absolute', top: 2, left: offline ? 18 : 2,
                width: 18, height: 18, borderRadius: '50%', background: '#fff',
                transition: 'left 0.2s cubic-bezier(0.2, 0.8, 0.2, 1)',
                boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
              }} />
            </div>
          </div>
        </div>

        {/* Stack technique */}
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 1.2, color: t.textMuted, textTransform: 'uppercase', padding: '12px 0 8px' }}>
          Stack technique
        </div>
        <div style={{ background: t.bgElevated, borderRadius: 10, border: `1px solid ${t.border}`, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 6 }}>
          {[
            { label: '🔥 Firebase Auth + Firestore', status: 'Configuré' },
            { label: '🎙️ Whisper IA Vocale', status: 'À connecter' },
            { label: '📄 PDF Factur-X 2026', status: 'En cours' },
            { label: '⚡ React + Vite', status: 'Actif' },
          ].map((item, i) => (
            <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <span style={{ fontSize: 12, fontWeight: 600, color: t.text }}>{item.label}</span>
              <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 7px', borderRadius: 4,
                background: item.status === 'Actif' || item.status === 'Configuré' ? 'rgba(16,185,129,0.12)' : 'rgba(255,107,0,0.12)',
                color: item.status === 'Actif' || item.status === 'Configuré' ? '#10B981' : t.safety,
              }}>
                {item.status}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { AfPaiements, AfClientsList, AfQuoteDetail, AfSettings });
