// InstantDevis — Écran Instant Note (dictée vocale)
// Sauvegarde réelle via AF_STORE → localStorage + Firestore (si configuré)

function AfNoteFlash({ t, currency, onBack, onSave, setView }) {
  const [phase, setPhase] = React.useState('idle'); // idle | recording | processing | preview | saved
  const [transcript, setTranscript] = React.useState('');
  const [items, setItems] = React.useState([]);
  const [photos, setPhotos] = React.useState([]);
  const [clientName, setClientName] = React.useState('');
  const [trade, setTrade] = React.useState('multi');
  const [saving, setSaving] = React.useState(false);
  const [savedDevis, setSavedDevis] = React.useState(null);
  const [errorMsg, setErrorMsg] = React.useState('');
  
  const timerRef = React.useRef(null);
  const fallbackTextRef = React.useRef('');
  const mediaRecorderRef = React.useRef(null);
  const audioChunksRef = React.useRef([]);
  const recognitionRef = React.useRef(null);
  
  const barCount = 14;

  // Textes de démo par métier
  const DEMO_TEXTS = {
    multi:       'Pose de 12 m² de carrelage salle de bain, fourniture comprise, avec joint époxy. Dépose de l\'ancien carrelage et évacuation des gravats. Main d\'œuvre 4 heures.',
    plomberie:   'Remplacement chauffe-eau 200 litres Atlantic, dépose ancienne installation, mise en conformité sécurité. Main d\'œuvre plombier 3 heures. Déplacement inclus.',
    electricite: 'Rénovation tableau électrique 3 rangées Legrand, pose 6 disjoncteurs différentiels 30mA, mise aux normes NF C 15-100. Main d\'œuvre électricien 5 heures.',
    peinture:    'Peinture acrylique mate salon 35 m², préparation murs ponçage et rebouchage, deux couches application rouleau. Fourniture peinture Tollens pro comprise.',
  };

  // Items générés par métier (simulation IA Whisper)
  const DEMO_ITEMS = {
    multi: [
      { id: 1, label: 'Pose carrelage salle de bain', qty: 12, unit: 'm²', pu: 42, tva: 10 },
      { id: 2, label: 'Dépose ancien carrelage + évacuation gravats', qty: 12, unit: 'm²', pu: 18, tva: 10 },
      { id: 3, label: 'Main d\'œuvre carreleur', qty: 4, unit: 'h', pu: 55, tva: 20 },
    ],
    plomberie: [
      { id: 1, label: 'Chauffe-eau 200L Atlantic', qty: 1, unit: 'pce', pu: 489, tva: 20 },
      { id: 2, label: 'Dépose ancienne installation', qty: 1, unit: 'forfait', pu: 120, tva: 20 },
      { id: 3, label: 'Main d\'œuvre plombier', qty: 3, unit: 'h', pu: 55, tva: 20 },
      { id: 4, label: 'Déplacement', qty: 1, unit: 'forfait', pu: 40, tva: 20 },
    ],
    electricite: [
      { id: 1, label: 'Tableau électrique 3 rangées Legrand', qty: 1, unit: 'pce', pu: 268, tva: 10 },
      { id: 2, label: 'Disjoncteur différentiel 30mA', qty: 6, unit: 'pce', pu: 85, tva: 10 },
      { id: 3, label: 'Main d\'œuvre électricien', qty: 5, unit: 'h', pu: 62, tva: 20 },
    ],
    peinture: [
      { id: 1, label: 'Peinture acrylique Tollens pro 10L', qty: 4, unit: 'pot', pu: 72, tva: 10 },
      { id: 2, label: 'Préparation murs (ponçage + rebouchage)', qty: 35, unit: 'm²', pu: 12, tva: 10 },
      { id: 3, label: 'Application 2 couches rouleau', qty: 35, unit: 'm²', pu: 18, tva: 10 },
    ],
  };

  const DEMO_TITLES = {
    multi:       'Pose carrelage salle de bain 12m²',
    plomberie:   'Remplacement chauffe-eau 200L + MEC',
    electricite: 'Rénovation tableau électrique NF C15-100',
    peinture:    'Peinture acrylique salon 35m²',
  };

  const getVocalWorkerBase = () => {
    const fromIntegrationWorkers = (typeof AF_INTEGRATION !== 'undefined' && AF_INTEGRATION.workersBase)
      ? String(AF_INTEGRATION.workersBase).trim()
      : '';
    const fromProfileWorkers = typeof AF_PROFILE_STORE !== 'undefined'
      ? (AF_PROFILE_STORE.get().workersBaseUrl || '').trim()
      : '';
    const fromProfileVocal = typeof AF_PROFILE_STORE !== 'undefined'
      ? (AF_PROFILE_STORE.get().vocalWorkerBaseUrl || '').trim()
      : '';
    const fromIntVocal = (typeof AF_INTEGRATION !== 'undefined' && AF_INTEGRATION.vocalWorkerBase)
      ? String(AF_INTEGRATION.vocalWorkerBase).trim()
      : '';
    // Aligné sur AF_WORKER._baseAcoustic : workersBase (tokens) → workersBaseUrl (profil) → vocal…
    return fromIntegrationWorkers || fromProfileWorkers || fromProfileVocal || fromIntVocal || '';
  };

  const fallbackProcessLocal = (currentText) => {
    if (typeof AF_CATALOG_STORE === 'undefined') return DEMO_ITEMS[trade] || DEMO_ITEMS.multi;
    const lower = currentText.toLowerCase();
    const allItems = AF_CATALOG_STORE.getAll();
    const found = [];
    for (const item of allItems) {
      const hasKeyword = (item.keywords || []).some(kw => lower.includes(kw.toLowerCase()));
      const hasLabelWord = item.label.toLowerCase().split(' ').some(w => w.length > 4 && lower.includes(w));
      if (hasKeyword || hasLabelWord) found.push({ ...item, qty: 1, id: `temp-${Date.now()}-${Math.random()}` });
    }
    const res = found.length > 0 ? found : (DEMO_ITEMS[trade] || DEMO_ITEMS.multi);
    return res.map(r => ({ ...r, id: r.id.toString().startsWith('temp') ? r.id : `temp-${Date.now()}-${Math.random()}` }));
  };

  const processFallback = () => {
    const text = fallbackTextRef.current || transcript || DEMO_TEXTS[trade] || DEMO_TEXTS.multi;
    setTranscript(text);
    setItems(fallbackProcessLocal(text));
    setPhase('preview');
  };

  const processRecording = async () => {
    setPhase('processing_audio');
    const textFromSpeech = (fallbackTextRef.current || transcript || '').trim();
    const base = getVocalWorkerBase();

    if (base && navigator.onLine && typeof AF_WORKER !== 'undefined') {
      const rec = mediaRecorderRef.current;
      const mime = rec && rec.mimeType ? rec.mimeType : 'audio/webm';
      const audioBlob = new Blob(audioChunksRef.current, { type: mime });
      if (audioBlob.size > 0) {
        const wr = await AF_WORKER.vocalIngest(audioBlob, 'dictée.webm');
        if (wr.ok && wr.data) {
          const j = wr.data;
          const wtext = (j.transcript || j.text || '').trim();
          const rawItems = j.items || j.lignes || j.lines;
          if (rawItems && Array.isArray(rawItems) && rawItems.length) {
            const mapped = rawItems.map((it, idx) => ({
              id: it.id || `w-${Date.now()}-${idx}`,
              label: it.label || it.description || 'Ligne',
              qty: Number(it.qty != null ? it.qty : it.quantite) || 1,
              unit: it.unit || it.unite || 'u',
              pu: Number(it.pu != null ? it.pu : it.prixUnitaire) || 0,
              tva: it.tva != null ? it.tva : 20,
            }));
            setTranscript(wtext || textFromSpeech);
            setItems(mapped);
            setPhase('preview');
            return;
          }
          if (wtext) {
            setTranscript(wtext);
            setItems(fallbackProcessLocal(wtext));
            setPhase('preview');
            return;
          }
        }
        if (!wr.skipped) {
          if (wr.userMessage) {
            setErrorMsg(wr.userMessage);
          } else {
            setErrorMsg(wr.error === 'timeout' ? 'Worker vocal : délai dépassé.' : 'Worker vocal indisponible. Mode local.');
          }
        }
      }
    }

    if (!textFromSpeech) {
      setErrorMsg('Aucune transcription. Démo catalogue appliquée.');
    }
    setTranscript(textFromSpeech || DEMO_TEXTS[trade] || DEMO_TEXTS.multi);
    setItems(fallbackProcessLocal(textFromSpeech || DEMO_TEXTS[trade] || DEMO_TEXTS.multi));
    setPhase('preview');
  };

  const startRecording = async () => {
    setPhase('recording');
    setErrorMsg('');
    setTranscript('');
    setItems([]);
    fallbackTextRef.current = '';
    audioChunksRef.current = [];

    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (SpeechRecognition) {
      recognitionRef.current = new SpeechRecognition();
      recognitionRef.current.lang = 'fr-FR';
      recognitionRef.current.continuous = true;
      recognitionRef.current.interimResults = true;
      recognitionRef.current.onresult = (e) => {
        let finalTrans = '';
        for (let i = e.resultIndex; i < e.results.length; ++i) {
          if (e.results[i].isFinal) finalTrans += e.results[i][0].transcript + ' ';
        }
        fallbackTextRef.current += finalTrans;
        setTranscript(prev => prev + finalTrans);
      };
      try { recognitionRef.current.start(); } catch(e){}
    }

    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mediaRecorder = new MediaRecorder(stream);
      mediaRecorderRef.current = mediaRecorder;
      mediaRecorder.ondataavailable = e => {
        if (e.data.size > 0) audioChunksRef.current.push(e.data);
      };
      mediaRecorder.start();
    } catch(err) {
      console.warn('Microphone non accessible pour MediaRecorder', err);
    }
  };

  const stopRecording = () => {
    if (recognitionRef.current) {
      try { recognitionRef.current.stop(); } catch(e){}
    }
    
    if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
      mediaRecorderRef.current.onstop = async () => {
        mediaRecorderRef.current.stream.getTracks().forEach(t => t.stop());
        await processRecording();
      };
      mediaRecorderRef.current.stop();
    } else {
      processFallback();
    }
  };

  const reset = () => {
    if (timerRef.current) clearTimeout(timerRef.current);
    setSaving(false);
    setSavedDevis(null);
    setPhase('idle');
    setTranscript('');
    setItems([]);
    setPhotos([]);
  };

  const handleAddPhoto = (e) => {
    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);
        setPhotos(p => [...p, { id: Date.now(), data: dataUrl }]);
      };
      img.src = event.target.result;
    };
    reader.readAsDataURL(file);
  };

  React.useEffect(() => () => {
    if (timerRef.current) clearTimeout(timerRef.current);
    // Libère le micro si le composant est démonté pendant l'enregistrement
    if (recognitionRef.current) {
      try { recognitionRef.current.stop(); } catch (e) {}
    }
    if (mediaRecorderRef.current) {
      const mr = mediaRecorderRef.current;
      if (mr.state !== 'inactive') {
        try { mr.stop(); } catch (e) {}
      }
      // Libère la piste audio (éteint le micro)
      if (mr.stream) {
        try { mr.stream.getTracks().forEach(t => t.stop()); } catch (e) {}
      }
    }
  }, []);

  // ── Calculs financiers (même moteur que addDevis : AF_DEVIS.calcFromLignes + acomptePercent profil) ──
  const acomptePercentForUi = typeof AF_PROFILE_STORE !== 'undefined'
    ? (AF_PROFILE_STORE.get().acomptePercent ?? 30)
    : 30;
  const calcLines = items && items.length && typeof AF_DEVIS !== 'undefined' && AF_DEVIS.calcFromLignes
    ? AF_DEVIS.calcFromLignes(items)
    : null;
  const totalHT = calcLines
    ? calcLines.totalHT
    : items.reduce((s, it) => s + it.qty * it.pu, 0);
  const tvaGroups = calcLines
    ? calcLines.tvaByRate
    : items.reduce((acc, it) => {
        const rate = it.tva !== undefined ? it.tva : 20;
        const amt = it.qty * it.pu * (rate / 100);
        acc[rate] = (acc[rate] || 0) + amt;
        return acc;
      }, {});
  const totalTVA = calcLines
    ? calcLines.totalTVA
    : Object.values(tvaGroups).reduce((a, b) => a + b, 0);
  const totalTTC = calcLines
    ? calcLines.totalTTC
    : totalHT + totalTVA;
  const acompte = calcLines
    ? calcLines.acompte
    : Math.round(totalTTC * (acomptePercentForUi / 100) * 100) / 100;
  const marge = Math.round(totalHT * 0.35);

  // ── Sauvegarde réelle dans AF_STORE ──────────────────────────
  const handleSave = async () => {
    if (saving) return;
    setSaving(true);
    try {
      const devis = await AF_STORE.addDevis({
        clientName: clientName.trim() || 'Client chantier',
        title:      DEMO_TITLES[trade] || 'Devis vocal',
        trade,
        items,
        totalHT:   Math.round(totalHT * 100) / 100,
        totalTVA:  Math.round(totalTVA * 100) / 100,
        totalTTC:  Math.round(totalTTC * 100) / 100,
        acompte,
        photos,
      });
      setSavedDevis(devis);
      if (devis && devis.localSync) {
        const st = devis.localSync.state;
        if (st === 'synced') {
          setErrorMsg('');
        } else if (st === 'error') {
          setErrorMsg('Devis enregistré en local. Erreur cloud : ' + (devis.localSync.detail || 'inconnue'));
        } else if (st === 'local_only') {
          setErrorMsg(devis.localSync.detail
            ? ('Devis enregistré. ' + devis.localSync.detail)
            : 'Devis enregistré en local (pas de synchronisation cloud).');
        } else {
          setErrorMsg('');
        }
        if (typeof AF_STORE.clearLastFirestoreError === 'function') AF_STORE.clearLastFirestoreError();
      } else {
        const fsErr = typeof AF_STORE.getLastFirestoreError === 'function' ? AF_STORE.getLastFirestoreError() : null;
        if (fsErr) {
          const off = typeof navigator !== 'undefined' && !navigator.onLine;
          setErrorMsg(
            off
              ? ('Devis enregistré en local. Sans réseau, la synchronisation cloud est différée. Détail : ' + fsErr)
              : ('Devis enregistré en local. Synchronisation cloud : ' + fsErr)
          );
          if (typeof AF_STORE.clearLastFirestoreError === 'function') AF_STORE.clearLastFirestoreError();
        } else {
          setErrorMsg('');
        }
      }
      setPhase('saved');
    } catch (err) {
      console.error('[InstantDevis] Erreur sauvegarde:', err);
    } finally {
      setSaving(false);
    }
  };

  const handleGoToDashboard = () => {
    setSaving(false);
    setSavedDevis(null);
    onSave({ saved: true });   // remonte vers app.jsx → goBack() → dashboard
  };

  // ── Render ────────────────────────────────────────────────────
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflowY: 'auto', background: t.bg }}>
      <AfScreenHeader
        title="⚡ Instant Note"
        subtitle="Dictée vocale → Devis"
        t={t} back onBack={onBack} tape
      />

      {errorMsg && (
        <div style={{ margin: '12px 16px 0', padding: '10px 12px', borderRadius: 8, background: 'rgba(255,107,0,0.1)', border: `1px solid ${t.safety}`, fontSize: 12, color: t.safety, fontWeight: 600, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div>⚠️ {errorMsg}</div>
          <button onClick={() => processRecording()} style={{
            background: t.safety, color: '#fff', border: 'none', padding: '4px 8px', borderRadius: 6, fontSize: 11, fontWeight: 700, cursor: 'pointer'
          }}>
            Réessayer
          </button>
        </div>
      )}

      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', padding: '14px 16px 20px', gap: 12 }}>

        {/* ── PHASE : IDLE / RECORDING ── */}
        {(phase === 'idle' || phase === 'recording') && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12, animation: 'af-slide-up 0.25s both' }}>

            {/* Client */}
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: t.textMuted, textTransform: 'uppercase', letterSpacing: 0.6, display: 'block', marginBottom: 6 }}>
                Client / Chantier
              </label>
              <input
                value={clientName}
                onChange={e => setClientName(e.target.value)}
                placeholder="Ex : Mme Dupont — Salle de bain"
                style={{
                  width: '100%', padding: '12px 14px', borderRadius: 8,
                  background: t.bgElevated, border: `1.5px solid ${t.border}`,
                  fontSize: 14, fontWeight: 600, color: t.text, outline: 'none',
                }}
              />
            </div>

            {/* Métier */}
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: t.textMuted, textTransform: 'uppercase', letterSpacing: 0.6, display: 'block', marginBottom: 6 }}>
                Métier
              </label>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {Object.entries(AF_TRADE_META).map(([key, meta]) => (
                  <button key={key} onClick={() => setTrade(key)} style={{
                    padding: '7px 12px', borderRadius: 20, fontSize: 12, fontWeight: 700,
                    border: `1.5px solid ${trade === key ? meta.color : t.border}`,
                    background: trade === key ? meta.color + '22' : t.bgElevated,
                    color: trade === key ? meta.color : t.textMuted,
                    cursor: 'pointer', transition: 'all 0.15s',
                  }}>
                    {meta.label}
                  </button>
                ))}
              </div>
            </div>

            {/* Visualiseur onde */}
            <div style={{
              height: 72, borderRadius: 10,
              background: t.bgElevated,
              border: `1.5px solid ${phase === 'recording' ? t.safety : t.border}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 3,
              transition: 'border-color 0.3s', overflow: 'hidden',
            }}>
              {Array.from({ length: barCount }).map((_, i) => (
                <div key={i} style={{
                  width: 4, borderRadius: 2, minHeight: 4,
                  background: phase === 'recording' ? t.safety : t.border,
                  animation: phase === 'recording'
                    ? `af-wave ${0.35 + i * 0.05}s ${i * 0.03}s ease-in-out infinite alternate`
                    : 'none',
                  height: phase === 'recording' ? undefined : '6px',
                  transition: 'background 0.3s',
                }} />
              ))}
              {phase === 'idle' && (
                <span style={{ fontSize: 11, color: t.textMuted, fontWeight: 600, marginLeft: 6, position: 'absolute' }}>
                  Prêt à enregistrer
                </span>
              )}
            </div>

            {/* Transcription live */}
            {transcript && (
              <div style={{
                background: t.bgElevated, borderRadius: 10,
                border: `1px solid ${t.border}`,
                padding: '10px 14px', fontSize: 13, lineHeight: 1.6, color: t.text,
              }}>
                <span style={{ opacity: 0.5, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.8, display: 'block', marginBottom: 4 }}>
                  Transcription en direct
                </span>
                {transcript}
                <span style={{
                  display: 'inline-block', width: 2, height: 14,
                  background: t.safety, marginLeft: 2, verticalAlign: 'middle',
                  animation: 'af-pulse 0.7s infinite',
                }} />
              </div>
            )}

            {/* Bouton micro */}
            {phase === 'idle' ? (
              <button id="btn-record" onClick={startRecording} style={{
                width: '100%', height: 76, borderRadius: 14,
                background: `linear-gradient(135deg, ${t.safety} 0%, #ff8c00 100%)`,
                border: 'none', cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12,
                fontSize: 17, fontWeight: 800, color: '#0D1520',
                boxShadow: '0 8px 24px rgba(255,107,0,0.4)',
                transition: 'transform 0.1s',
                position: 'relative', overflow: 'hidden',
              }}
                onMouseDown={e => e.currentTarget.style.transform = 'scale(0.97)'}
                onMouseUp={e => e.currentTarget.style.transform = 'scale(1)'}
                onMouseLeave={e => e.currentTarget.style.transform = 'scale(1)'}
              >
                <div style={{
                  position: 'absolute', inset: 0, opacity: 0.07,
                  backgroundImage: 'repeating-linear-gradient(-45deg, #fff 0, #fff 2px, transparent 2px, transparent 10px)',
                }}/>
                <lucide.Mic size={30} strokeWidth={2.5} />
                Appuyer pour dicter
              </button>
            ) : (
              <button id="btn-stop" onClick={stopRecording} style={{
                width: '100%', height: 76, borderRadius: 14,
                background: '#B91C1C', border: 'none', cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12,
                fontSize: 16, fontWeight: 800, color: '#fff',
                boxShadow: '0 8px 24px rgba(185,28,28,0.4)',
                animation: 'af-pulse 1.8s infinite',
              }}>
                <lucide.Mic size={28} strokeWidth={2.5} />
                ● Enregistrement… Stopper
              </button>
            )}
          </div>
        )}

        {/* ── PHASE : PROCESSING (IA) ── */}
        {phase === 'processing_audio' && (
          <div style={{
            flex: 1, display: 'flex', flexDirection: 'column',
            alignItems: 'center', justifyContent: 'center', gap: 16,
            animation: 'af-slide-up 0.25s both', minHeight: 300,
          }}>
            <div style={{
              width: 72, height: 72, borderRadius: '50%',
              background: t.bgElevated, border: `2px solid ${t.border}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <lucide.Sparkles size={30} color={t.safety} style={{ animation: 'af-pulse 0.7s infinite' }} />
            </div>
            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 16, fontWeight: 800, color: t.text }}>
                {getVocalWorkerBase() && navigator.onLine ? 'Transcription (Worker)…' : 'Analyse locale (catalogue)…'}
              </div>
              <div style={{ fontSize: 12, color: t.textMuted, marginTop: 4 }}>Extraction des lignes de devis</div>
            </div>
            {/* Étapes */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6, width: '100%', maxWidth: 260 }}>
              {['Transcription audio', 'Parsing entités & Catalogue', 'Génération devis'].map((step, i) => {
                const isActive = i <= 1;
                return (
                  <div key={i} style={{
                    display: 'flex', alignItems: 'center', gap: 8,
                    fontSize: 12, color: t.textMuted, fontWeight: 600,
                    opacity: isActive ? 1 : 0.3,
                  }}>
                    <div style={{
                      width: 16, height: 16, borderRadius: '50%',
                      background: isActive ? 'rgba(255,107,0,0.15)' : 'transparent',
                      border: `1.5px solid ${isActive ? t.safety : t.border}`,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      animation: isActive ? 'af-pulse 1s infinite' : 'none',
                    }}>
                      <div style={{ width: 5, height: 5, borderRadius: '50%', background: isActive ? t.safety : 'transparent' }} />
                    </div>
                    {step}
                  </div>
                );
              })}
            </div>
          </div>
        )}

        {/* ── PHASE : PREVIEW (devis généré) ── */}
        {phase === 'preview' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, animation: 'af-slide-up 0.3s both' }}>
            {/* Badge IA */}
            <div style={{
              display: 'flex', alignItems: 'center', gap: 8,
              padding: '8px 12px', borderRadius: 8,
              background: 'rgba(255,107,0,0.1)', border: `1px solid rgba(255,107,0,0.3)`,
            }}>
              <lucide.Sparkles size={14} color={t.safety} />
              <span style={{ fontSize: 12, fontWeight: 700, color: t.safety }}>
                IA a généré {items.length} lignes — vérifiez avant d'envoyer
              </span>
            </div>

            {/* Champ client si vide */}
            {!clientName && (
              <input
                value={clientName}
                onChange={e => setClientName(e.target.value)}
                placeholder="Nom du client (requis)"
                style={{
                  width: '100%', padding: '10px 14px', borderRadius: 8,
                  background: t.bgElevated,
                  border: `1.5px solid ${t.safety}`,
                  fontSize: 14, fontWeight: 600, color: t.text, outline: 'none',
                }}
              />
            )}

            {/* Lignes devis */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {items.map(it => (
                <div key={it.id} style={{
                  background: t.bgElevated, borderRadius: 10,
                  border: `1px solid ${t.border}`, padding: '11px 14px',
                }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: t.text, marginBottom: 5 }}>
                    {it.label}
                  </div>
                  <div style={{
                    display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                    fontSize: 12, color: t.textMuted,
                  }}>
                    <span>{it.qty} {it.unit} × {it.pu} € HT</span>
                    <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                      <span style={{
                        fontSize: 10, padding: '2px 6px', borderRadius: 4,
                        background: `rgba(11,61,92,0.1)`, color: t.primary, fontWeight: 700,
                      }}>TVA {it.tva}%</span>
                      <span className="af-mono" style={{ fontSize: 14, fontWeight: 800, color: t.text }}>
                        {(it.qty * it.pu).toLocaleString('fr-FR')} €
                      </span>
                    </div>
                  </div>
                </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>
                <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>
              {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' }} />
                      <button onClick={() => setPhotos(curr => curr.filter(x => x.id !== 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>

            {/* Bloc totaux */}
            <div style={{
              background: t.primary, borderRadius: 10,
              padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 5,
            }}>
              {[
                { label: 'Total HT', value: `${totalHT.toLocaleString('fr-FR')} €`, dim: true },
                ...Object.entries(tvaGroups).sort(([a], [b]) => Number(a) - Number(b)).map(([rate, amt]) => ({
                  label: `TVA ${rate}%`, value: `${amt.toFixed(2)} €`, dim: true
                })),
              ].map(r => (
                <div key={r.label} style={{ display: 'flex', justifyContent: 'space-between', color: 'rgba(255,255,255,0.65)', fontSize: 12 }}>
                  <span>{r.label}</span>
                  <span className="af-mono">{r.value}</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">{totalTTC.toFixed(2)} €</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', color: t.safety, fontSize: 13, fontWeight: 700 }}>
                <span>Acompte {acomptePercentForUi} %</span>
                <span className="af-mono">{acompte.toFixed(2)} €</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', color: '#34D399', fontSize: 12, fontWeight: 600 }}>
                <span>Marge brute est.</span>
                <span className="af-mono">+{marge.toLocaleString('fr-FR')} €</span>
              </div>
            </div>

            {/* Actions */}
            <div style={{ display: 'flex', gap: 8 }}>
              <button onClick={reset} style={{
                flex: 1, height: 52, borderRadius: 10,
                background: t.bgElevated, border: `1.5px solid ${t.border}`,
                fontSize: 13, fontWeight: 700, color: t.text, cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
              }}>
                <lucide.RotateCcw size={15} />
                Refaire
              </button>
              <button
                id="btn-save-devis"
                onClick={handleSave}
                disabled={saving}
                style={{
                  flex: 2, height: 52, borderRadius: 10,
                  background: saving ? t.bgSunken : t.safety,
                  border: 'none', fontSize: 14, fontWeight: 800,
                  color: saving ? t.textMuted : '#0D1520',
                  cursor: saving ? 'not-allowed' : 'pointer',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
                  boxShadow: saving ? 'none' : '0 4px 16px rgba(255,107,0,0.4)',
                  transition: 'all 0.2s',
                }}
              >
                {saving
                  ? <><lucide.Loader size={16} style={{ animation: 'af-pulse 0.8s infinite' }} /> Sauvegarde…</>
                  : <><lucide.CloudUpload size={16} /> Sauvegarder</>
                }
              </button>
            </div>
          </div>
        )}

        {/* ── PHASE : SAVED (confirmation) ── */}
        {phase === 'saved' && savedDevis && (() => {
          const _sdOut = (typeof AF_DEVIS !== 'undefined' && AF_DEVIS.documentOutput) ? AF_DEVIS.documentOutput(savedDevis) : null;
          const _sdTTC = _sdOut ? _sdOut.totalTTC : (Number(savedDevis.totalTTC != null ? savedDevis.totalTTC : savedDevis.amount) || 0);
          const _sdAc = _sdOut
            ? _sdOut.acompte
            : (savedDevis.acompte != null
                ? savedDevis.acompte
                : Math.round(_sdTTC * (acomptePercentForUi / 100) * 100) / 100);
          return (
          <div style={{
            display: 'flex', flexDirection: 'column', alignItems: 'center',
            gap: 16, paddingTop: 24, animation: 'af-slide-up 0.3s both',
          }}>
            {/* Icône succès */}
            <div style={{
              width: 80, height: 80, borderRadius: '50%',
              background: 'rgba(16,185,129,0.12)',
              border: '2px solid rgba(16,185,129,0.5)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <lucide.CheckCircle2 size={40} color="#10B981" />
            </div>

            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 18, fontWeight: 800, color: t.text }}>Devis sauvegardé !</div>
              <div style={{ fontSize: 12, color: t.textMuted, marginTop: 4 }}>
                {AF_STORE.isFirebaseReady
                  ? '✅ Synchronisé sur Firebase Firestore'
                  : '💾 Enregistré en local (localStorage)'}
              </div>
            </div>

            {/* Récapitulatif */}
            <div style={{
              width: '100%', background: t.bgElevated, borderRadius: 12,
              border: `1.5px solid rgba(16,185,129,0.4)`,
              padding: '16px',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 800, color: t.text }}>{_sdOut && _sdOut.title ? _sdOut.title : savedDevis.title}</div>
                  <div style={{ fontSize: 11, color: t.textMuted, marginTop: 2 }}>{_sdOut && _sdOut.clientName ? _sdOut.clientName : savedDevis.clientName}</div>
                </div>
                <span className="af-mono" style={{ fontSize: 15, fontWeight: 800, color: '#10B981' }}>
                  {_sdTTC.toFixed(2)} €
                </span>
              </div>
              {[
                { label: 'Référence', value: savedDevis.id, mono: true },
                { label: `Acompte ${acomptePercentForUi} %`, value: `${_sdAc.toFixed(2)} €`, color: t.safety },
                { label: 'Statut', value: 'Brouillon' },
                { label: 'Lignes', value: `${savedDevis.lignes?.length || 0} postes` },
              ].map(row => (
                <div key={row.label} style={{
                  display: 'flex', justifyContent: 'space-between',
                  padding: '6px 0', borderTop: `1px solid ${t.border}`,
                  fontSize: 12,
                }}>
                  <span style={{ color: t.textMuted, fontWeight: 600 }}>{row.label}</span>
                  <span className={row.mono ? 'af-mono' : ''} style={{
                    fontWeight: 700, color: row.color || t.text,
                  }}>
                    {row.value}
                  </span>
                </div>
              ))}
            </div>

            {/* Bouton retour dashboard */}
            <button
              id="btn-go-dashboard"
              onClick={handleGoToDashboard}
              style={{
                width: '100%', height: 58, borderRadius: 12,
                background: t.primary, border: 'none', cursor: 'pointer',
                fontSize: 15, fontWeight: 800, color: '#fff',
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
                boxShadow: '0 6px 20px rgba(11,61,92,0.3)',
              }}
            >
              <lucide.Home size={18} />
              Voir sur le Dashboard
            </button>

            <button onClick={reset} style={{
              fontSize: 13, fontWeight: 600, color: t.textMuted,
              background: 'none', border: 'none', cursor: 'pointer',
              textDecoration: 'underline',
            }}>
              Créer un autre devis
            </button>
          </div>
          );
        })()}
      </div>
    </div>
  );
}

Object.assign(window, { AfNoteFlash });
