/* global React, VISITES, VT_HERO, VT_HERO_EQUIPMENTS, VT_HERO_TRANSCRIPT, VT_CATEGORIES, Badge, Btn, Dot, Icon, RefChip, Sigil, Sparkline, BarSpark, EquipIcon, Waveform, VideoFrame, Confidence, RoofMap, formatTS, formatDuration, fmtMWh, fmtEur, fmtInt */
// Visites — pipeline list + hub fiche 360° + review desktop

const { useState: _vtUseS, useEffect: _vtUseE, useRef: _vtUseR, useMemo: _vtUseM } = React;

// ─── Status mapping ──────────────────────────────────
const VT_STATUS_META = {
  draft:      { label: 'Brouillon',   tone: 'muted',   dot: 'muted',  desc: 'À préparer' },
  recording:  { label: 'En cours',    tone: 'rouge',   dot: 'rouge',  desc: 'Enregistrement live' },
  processing: { label: 'Analyse IA',  tone: 'plasma',  dot: 'plasma', desc: 'Vision + NLP en cours' },
  completed:  { label: 'Clôturée',    tone: 'signal',  dot: 'signal', desc: 'Rapport disponible' },
  failed:     { label: 'Échec',       tone: 'rouge',   dot: 'rouge',  desc: 'Erreur de traitement' },
};

// ─── Pipeline page ───────────────────────────────────
function VisitesPage({ onOpenVisite, onStartNew }) {
  const [view, setView] = _vtUseS('list'); // list | map | tech
  const [filterStatus, setFilterStatus] = _vtUseS('all');
  const [filterTech, setFilterTech] = _vtUseS('all');

  const filtered = VISITES.filter(v => (filterStatus === 'all' || v.status === filterStatus) && (filterTech === 'all' || v.technicianEmail === filterTech));

  const completed = VISITES.filter(v => v.status === 'completed');
  const stats = {
    total: VISITES.length,
    today: VISITES.filter(v => v.startedAt && new Date(v.startedAt).toDateString() === new Date().toDateString()).length,
    recording: VISITES.filter(v => v.status === 'recording').length,
    processing: VISITES.filter(v => v.status === 'processing').length,
    avgDuration: completed.length > 0
      ? Math.round(completed.reduce((s, v) => s + (v.durationSeconds || 0), 0) / completed.length)
      : 0,
    totalCumacReco: VISITES.reduce((s, v) => s + (v.cumacReco || 0), 0),
  };

  return (
    <div style={{ padding: '20px 24px 40px' }}>
      {/* Hero header */}
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16, marginBottom: 18 }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.8, fontWeight: 600 }}>Terrain · Capture IA</div>
          <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '4px 0 0' }}>Visites techniques</h1>
          <div style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 4 }}>Vidéo + audio analysés par vision · OCR plaques · NLP multi-locuteurs. Alimente automatiquement les fiches FOST, le CRM et le calpinage.</div>
        </div>
        <div style={{ flex: 1 }} />
        <Btn variant="outline" size="sm" icon={<Icon.refresh />}>Synchroniser</Btn>
        <Btn variant="signal" size="sm" icon={<Icon.plus />} onClick={onStartNew}>Nouvelle visite</Btn>
        <VisiteSettingsButton />
      </div>

      {/* Stat strip */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 1, border: '1px solid var(--line)', borderRadius: 6, background: 'var(--line)', marginBottom: 22 }}>
        <Stat label="Visites ce mois" value={stats.total} mono bg/>
        <Stat label="Aujourd'hui" value={stats.today} mono bg tone={stats.today > 0 ? 'signal' : 'muted'}/>
        <Stat label="En cours (live)" value={stats.recording} mono bg tone="rouge" pulse={stats.recording > 0}/>
        <Stat label="En analyse" value={stats.processing} mono bg tone="plasma"/>
        <Stat label="Durée moyenne" value={formatDuration(stats.avgDuration)} bg/>
        <Stat label="Cumac potentiel" value={fmtMWh(stats.totalCumacReco)} bg/>
      </div>

      {/* Controls */}
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12 }}>
        <Segmented value={view} onChange={setView} options={[
          { val: 'list', label: 'Liste' },
          { val: 'map', label: 'Carte' },
          { val: 'tech', label: 'Par technicien' },
        ]}/>
        <span style={{ flex: 1 }}/>
        <FilterChip label="Statut" value={filterStatus} onChange={setFilterStatus} options={[
          { val: 'all', label: 'Tous' },
          ...Object.entries(VT_STATUS_META).map(([k, m]) => ({ val: k, label: m.label })),
        ]}/>
        <FilterChip label="Technicien" value={filterTech} onChange={setFilterTech} options={[
          { val: 'all', label: 'Tous' },
          ...Array.from(new Set(VISITES.map(v => v.technicianEmail))).map(e => ({ val: e, label: VISITES.find(v => v.technicianEmail === e)?.technicianName || e })),
        ]}/>
      </div>

      {view === 'list' && <VisitesList items={filtered} onOpenVisite={onOpenVisite} onDataChanged={() => window.AE_API?.hydrate?.()}/>}
      {view === 'map' && <VisitesMap items={filtered} onOpenVisite={onOpenVisite}/>}
      {view === 'tech' && <VisitesByTech items={filtered} onOpenVisite={onOpenVisite}/>}
    </div>
  );
}

function Stat({ label, value, tone, mono, pulse, bg }) {
  return (
    <div style={{ padding: '12px 14px', background: bg ? 'var(--paper)' : 'transparent' }}>
      <div style={{ fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 5 }}>
        {tone && <Dot tone={tone} pulse={pulse} size={6}/>}
        {label}
      </div>
      <div className={mono ? 'mono' : ''} style={{ fontSize: mono ? 18 : 16, fontWeight: 600, color: 'var(--ink)', marginTop: 3, letterSpacing: mono ? '-0.02em' : 0 }}>{value}</div>
    </div>
  );
}

function Segmented({ value, onChange, options }) {
  return (
    <div style={{ display: 'flex', padding: 2, background: 'var(--paper-2)', border: '1px solid var(--line)', borderRadius: 5 }}>
      {options.map(o => (
        <button key={o.val} onClick={() => onChange(o.val)} style={{
          padding: '5px 10px', fontSize: 12, fontWeight: 500,
          background: value === o.val ? 'var(--paper)' : 'transparent',
          color: value === o.val ? 'var(--ink)' : 'var(--ink-4)',
          border: value === o.val ? '1px solid var(--line)' : '1px solid transparent',
          borderRadius: 4,
        }}>{o.label}</button>
      ))}
    </div>
  );
}

function FilterChip({ label, value, onChange, options }) {
  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      <select value={value} onChange={e => onChange(e.target.value)} style={{
        appearance: 'none', padding: '5px 26px 5px 10px', fontSize: 12, fontWeight: 500,
        background: 'var(--paper)', color: 'var(--ink-2)',
        border: '1px solid var(--line-2)', borderRadius: 5, fontFamily: 'inherit',
      }}>
        {options.map(o => <option key={o.val} value={o.val}>{label} · {o.label}</option>)}
      </select>
      <svg width="9" height="9" viewBox="0 0 10 10" stroke="currentColor" strokeWidth="1.5" fill="none" style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: 'var(--ink-4)' }}><path d="M2 4l3 3 3-3"/></svg>
    </div>
  );
}

function VisitesList({ items, onOpenVisite, onDataChanged }) {
  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', overflow: 'hidden' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '130px 1fr 150px 130px 110px 110px 100px 120px', padding: '8px 14px', borderBottom: '1px solid var(--line)', background: 'var(--paper-2)', fontSize: 10, fontWeight: 600, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5 }}>
        <div>Référence</div>
        <div>Client · Adresse</div>
        <div>Technicien</div>
        <div>Date</div>
        <div>Durée</div>
        <div>Équipements</div>
        <div>Statut</div>
        <div>Actions</div>
      </div>
      {items.length === 0 ? (
        <div style={{ padding: '60px 20px', textAlign: 'center', color: 'var(--ink-4)' }}>
          <div style={{ fontSize: 36, marginBottom: 12 }}>📷</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 6 }}>Aucune visite terrain</div>
          <div style={{ fontSize: 12, maxWidth: 420, margin: '0 auto' }}>
            Vos visites techniques apparaîtront ici. Cliquez sur <strong>« Nouvelle visite »</strong> en haut à droite pour créer votre première capture terrain (vidéo, audio ou mixte) liée à un client du CRM.
          </div>
        </div>
      ) : items.map(v => (
        <VisiteRow key={v.ref} v={v} onClick={() => onOpenVisite(v)} onDataChanged={onDataChanged}/>
      ))}
    </div>
  );
}

function VisiteRow({ v, onClick, onDataChanged }) {
  const [hover, setHover] = _vtUseS(false);
  const [busy, setBusy] = _vtUseS(null); // 'delete' | 'crm' | null
  const [toast, setToast] = _vtUseS(null);
  const meta = VT_STATUS_META[v.status];
  const API = (window.AE_API && window.AE_API.BASE) || '';
  const backendId = v._backendId || (v._backend ? v.id : null);
  const hasBackend = !!backendId;

  const doDelete = async (e) => {
    e.stopPropagation();
    if (!hasBackend) { setToast('⚠ Visite démo'); setTimeout(() => setToast(null), 2000); return; }
    if (v.status !== 'draft') {
      if (!window.confirm(`Visite ${v.ref} n'est pas en brouillon (statut : ${v.status}). Supprimer quand même ?\n\nNote : le backend refusera si non-brouillon.`)) return;
    } else if (!window.confirm(`Supprimer la visite ${v.ref} ?`)) return;
    setBusy('delete');
    try {
      const r = await fetch(`${API}/api/visite/${backendId}`, { method: 'DELETE' }).then(r => r.json());
      if (r?.status !== 'ok') throw new Error(r?.message || 'Erreur');
      setToast('✓ Supprimée');
      if (onDataChanged) onDataChanged();
    } catch (err) {
      setToast(`⚠ ${err.message}`);
    }
    setBusy(null);
    setTimeout(() => setToast(null), 3000);
  };

  const doCrmPush = async (e) => {
    e.stopPropagation();
    if (!hasBackend) { setToast('⚠ Visite démo'); setTimeout(() => setToast(null), 2000); return; }
    setBusy('crm');
    try {
      const r = await fetch(`${API}/api/visite/${backendId}/crm/push`, { method: 'POST' }).then(r => r.json());
      if (r?.status !== 'ok') throw new Error(r?.message || 'Erreur');
      setToast('✓ Poussé vers CRM');
      if (onDataChanged) onDataChanged();
    } catch (err) {
      setToast(`⚠ ${err.message}`);
    }
    setBusy(null);
    setTimeout(() => setToast(null), 3000);
  };

  return (
    <div onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} style={{
      display: 'grid', gridTemplateColumns: '130px 1fr 150px 130px 110px 110px 100px 120px',
      padding: '10px 14px', borderBottom: '1px solid var(--hairline)',
      alignItems: 'center', cursor: 'pointer',
      background: hover ? 'var(--paper-2)' : 'var(--paper)',
      position: 'relative',
    }}>
      <RefChip r={v.ref}/>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--ink)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{v.client}</div>
        <div style={{ fontSize: 11, color: 'var(--ink-4)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{v.clientAddress}</div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
        <div style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--ink)', color: 'var(--paper)', display: 'grid', placeItems: 'center', fontSize: 9, fontWeight: 600 }}>{v.technicianInit}</div>
        <div style={{ fontSize: 12, color: 'var(--ink-2)', minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{v.technicianName}</div>
      </div>
      <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>
        {new Date(v.startedAt).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short' })}
        <span style={{ color: 'var(--ink-5)' }}> · </span>
        {new Date(v.startedAt).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}
      </div>
      <div className="mono" style={{ fontSize: 12, color: 'var(--ink-3)' }}>{formatDuration(v.durationSeconds)}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
        <EquipIcon cat="pac_air_eau" size={14} style={{ color: 'var(--ink-3)' }}/>
        <span className="mono" style={{ fontSize: 12, color: 'var(--ink-2)' }}>{v.equipmentsDetected}</span>
        {v.ocrResults > 0 && <span style={{ fontSize: 10, color: 'var(--plasma)', fontWeight: 600 }}>· OCR {v.ocrResults}</span>}
      </div>
      <Badge tone={meta.tone} icon={v.status === 'recording' ? <Dot tone="rouge" pulse/> : null}>{meta.label}</Badge>

      {/* Actions (opacité plus forte au hover) */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 4, justifyContent: 'flex-end', opacity: hover ? 1 : 0.35, transition: 'opacity 120ms' }}>
        {hasBackend && !v.crmSynced && (
          <button onClick={doCrmPush} disabled={busy === 'crm'} title="Pousser vers CRM Odoo"
            style={{ padding: '4px 6px', fontSize: 10, background: 'var(--paper)', border: '1px solid var(--line-2)', borderRadius: 3, cursor: 'pointer', color: 'var(--plasma)' }}>
            {busy === 'crm' ? '…' : '⟳ CRM'}
          </button>
        )}
        {hasBackend && v.crmSynced && (
          <span title={`Synchronisé${v.crmSyncedAt ? ' le ' + new Date(v.crmSyncedAt).toLocaleDateString('fr-FR') : ''}`} style={{ fontSize: 10, color: 'var(--signal-deep)' }}>✓ CRM</span>
        )}
        <button onClick={doDelete} disabled={busy === 'delete'} title={v.status === 'draft' ? 'Supprimer' : 'Supprimer (refusé si non-brouillon)'}
          style={{ padding: '4px 6px', fontSize: 11, background: 'var(--paper)', border: '1px solid var(--line-2)', borderRadius: 3, cursor: 'pointer', color: v.status === 'draft' ? 'var(--rouge)' : 'var(--ink-4)' }}>
          {busy === 'delete' ? '…' : '🗑'}
        </button>
        <Icon.arrow style={{ color: 'var(--ink-4)' }}/>
      </div>

      {/* Toast inline (remplace brièvement les actions en cas de feedback) */}
      {toast && (
        <div style={{
          position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)',
          padding: '4px 10px', fontSize: 10, fontFamily: 'var(--font-mono)',
          background: toast.startsWith('⚠') ? 'var(--rouge-tint)' : 'var(--signal-tint)',
          color: toast.startsWith('⚠') ? 'var(--rouge)' : 'var(--signal-deep)',
          borderRadius: 3, border: '1px solid ' + (toast.startsWith('⚠') ? 'var(--rouge)' : 'var(--signal-soft)'),
        }}>{toast}</div>
      )}
    </div>
  );
}

// Carte
function VisitesMap({ items, onOpenVisite }) {
  const [selected, setSelected] = _vtUseS(null);
  const w = 900, h = 520;
  // France bounding box approximate
  const project = (lat, lon) => {
    const x = ((lon + 5.5) / 14.5) * w;
    const y = h - ((lat - 41) / 10.5) * h;
    return { x: Math.max(10, Math.min(w - 10, x)), y: Math.max(10, Math.min(h - 10, y)) };
  };
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: 16 }}>
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: '#0c2417', overflow: 'hidden', position: 'relative' }}>
        <svg viewBox={`0 0 ${w} ${h}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
          <defs>
            <pattern id="gridp" width="28" height="28" patternUnits="userSpaceOnUse">
              <path d="M28 0H0v28" fill="none" stroke="rgba(127,255,183,0.06)" strokeWidth="0.5"/>
            </pattern>
          </defs>
          <rect width={w} height={h} fill="url(#gridp)"/>
          {/* France contour simplified */}
          <path d="M180 120 Q160 80 230 70 Q280 60 360 80 Q430 70 500 100 Q580 90 640 140 Q700 180 680 260 Q670 340 620 400 Q560 460 480 470 Q400 480 320 460 Q240 440 190 380 Q150 320 160 240 Q170 180 180 120 Z" fill="rgba(127,255,183,0.05)" stroke="rgba(127,255,183,0.3)" strokeWidth="1"/>
          {/* Pins */}
          {items.map((v, i) => {
            const p = project(v.lat, v.lon);
            const meta = VT_STATUS_META[v.status];
            const color = { signal: '#7fffb7', rouge: '#e66b5c', plasma: '#c896ff', muted: '#888' }[meta.dot] || '#888';
            return (
              <g key={v.ref} transform={`translate(${p.x},${p.y})`} style={{ cursor: 'pointer' }} onClick={() => { setSelected(v); }}>
                {v.status === 'recording' && <circle r="14" fill={color} opacity="0.2"><animate attributeName="r" values="8;20;8" dur="2s" repeatCount="indefinite"/><animate attributeName="opacity" values="0.6;0.05;0.6" dur="2s" repeatCount="indefinite"/></circle>}
                <circle r="5" fill={color} stroke="#0c2417" strokeWidth="1.5"/>
                {selected?.ref === v.ref && <circle r="10" fill="none" stroke={color} strokeWidth="1"/>}
              </g>
            );
          })}
        </svg>
        <div style={{ position: 'absolute', bottom: 10, left: 10, fontSize: 10, color: 'rgba(127,255,183,0.6)', fontFamily: 'var(--font-mono)' }}>
          France métropolitaine · {items.length} visites · live
        </div>
      </div>
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 14, maxHeight: 520, overflow: 'auto' }}>
        {selected ? (
          <>
            <RefChip r={selected.ref}/>
            <div style={{ fontSize: 14, fontWeight: 600, marginTop: 8 }}>{selected.client}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>{selected.clientAddress}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
              <Dot tone={VT_STATUS_META[selected.status].dot} size={6}/>
              {VT_STATUS_META[selected.status].label} · zone {selected.zone}
            </div>
            <div style={{ marginTop: 14 }}>
              <Btn variant="signal" size="sm" onClick={() => onOpenVisite(selected)} iconRight={<Icon.arrow/>}>Ouvrir</Btn>
            </div>
          </>
        ) : (
          <div style={{ fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', padding: 20 }}>
            Cliquez sur un point pour voir la visite.
          </div>
        )}
      </div>
    </div>
  );
}

// Vue par technicien
function VisitesByTech({ items, onOpenVisite }) {
  const techs = {};
  items.forEach(v => {
    if (!techs[v.technicianEmail]) techs[v.technicianEmail] = { name: v.technicianName, init: v.technicianInit, truck: v.truck, items: [] };
    techs[v.technicianEmail].items.push(v);
  });
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 14 }}>
      {Object.entries(techs).map(([email, t]) => {
        const live = t.items.find(v => v.status === 'recording');
        const totalMin = Math.round(t.items.reduce((s, v) => s + v.durationSeconds, 0) / 60);
        return (
          <div key={email} style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', overflow: 'hidden' }}>
            <div style={{ padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--hairline)' }}>
              <div style={{ position: 'relative', width: 34, height: 34, borderRadius: '50%', background: 'var(--ink)', color: 'var(--paper)', display: 'grid', placeItems: 'center', fontSize: 13, fontWeight: 600 }}>
                {t.init}
                {live && <span style={{ position: 'absolute', top: -2, right: -2, width: 10, height: 10, borderRadius: '50%', background: 'var(--rouge)', border: '2px solid var(--paper)', animation: 'dot-pulse 1.4s infinite' }}/>}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 600 }}>{t.name}</div>
                <div className="mono" style={{ fontSize: 10, color: 'var(--ink-4)' }}>camion {t.truck} · {t.items.length} visites · {totalMin} min</div>
              </div>
              {live && <Badge tone="rouge" icon={<Dot tone="rouge" pulse size={6}/>}>Live</Badge>}
            </div>
            <div style={{ maxHeight: 260, overflow: 'auto' }}>
              {t.items.slice(0, 6).map(v => (
                <div key={v.ref} onClick={() => onOpenVisite(v)} style={{ padding: '8px 14px', borderBottom: '1px solid var(--hairline)', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
                  <Dot tone={VT_STATUS_META[v.status].dot} size={6}/>
                  <span className="mono" style={{ fontSize: 10, color: 'var(--ink-4)', minWidth: 78 }}>{v.ref}</span>
                  <span style={{ flex: 1, color: 'var(--ink-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{v.client}</span>
                  <span className="mono" style={{ fontSize: 10, color: 'var(--ink-4)' }}>{formatDuration(v.durationSeconds)}</span>
                </div>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── NewVisiteModal ──────────────────────────────────
// Modal de création d'une visite technique (Étape 1 du plan de portage)
// Utilise ClientPicker (règle 3.a CLAUDE.md) pour sélection/création client
// POST /api/visite → retourne la visite créée → parent ouvre VisiteHub ou capture
function NewVisiteModal({ onClose, onCreated }) {
  const [client, setClient] = _vtUseS(null);
  const [type, setType] = _vtUseS('video');
  const [address, setAddress] = _vtUseS('');
  const [notes, setNotes] = _vtUseS('');
  const [saving, setSaving] = _vtUseS(false);
  const [error, setError] = _vtUseS('');

  // Pré-remplit l'adresse à partir du client choisi
  _vtUseE(() => {
    if (!client) return;
    const parts = [client.street, client.zip, client.city].filter(Boolean).join(' ');
    if (parts && !address) setAddress(parts);
  }, [client?.id, client?.source]);

  const canSubmit = !!client && !saving;

  async function submit() {
    if (!client) { setError('Sélectionnez un client'); return; }
    setSaving(true); setError('');
    try {
      const body = {
        type,
        clientAddress: address,
        contactId: client.contactId || null,
        organizationId: client.organizationId || null,
        notes: notes || null,
        metadata: {
          clientSource: client.source,
          clientName: client.name,
          odooId: client.odooId || null,
        },
      };
      const r = await fetch(((window.AE_API && window.AE_API.BASE) || '') + '/api/visite', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const data = await r.json();
      const visit = data.visit || data;
      // Propage le client dans les autres modules (règle 3.a)
      if (window.AE_CLIENT_PICKER && window.AE_CLIENT_PICKER.propagate) {
        window.AE_CLIENT_PICKER.propagate('tech_visit', visit.id, client).catch(() => {});
      }
      // Rehydrate globals (VISITES liste rafraîchie)
      if (window.AE_API && window.AE_API.hydrate) {
        window.AE_API.hydrate();
      }
      onCreated && onCreated(visit);
    } catch (e) {
      setError('Création impossible: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 120, background: 'rgba(10,14,18,0.5)',
      backdropFilter: 'blur(4px)', display: 'grid', placeItems: 'center', padding: 20,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: 'min(560px, 100%)', background: 'var(--paper)', border: '1px solid var(--line)',
        borderRadius: 8, boxShadow: '0 20px 60px -12px rgba(0,0,0,0.35)', overflow: 'hidden',
      }}>
        <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--hairline)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.7, fontWeight: 600 }}>Nouvelle capture terrain</div>
            <div style={{ fontSize: 15, fontWeight: 600, marginTop: 2 }}>Visite technique</div>
          </div>
          <button onClick={onClose} style={{ padding: '4px 8px', fontSize: 12, background: 'transparent', border: '1px solid var(--line-2)', borderRadius: 4, cursor: 'pointer' }}>Fermer</button>
        </div>

        <div style={{ padding: 18, display: 'grid', gap: 14 }}>
          {/* Client — ClientPicker unifié */}
          <div>
            <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--ink-3)', marginBottom: 5, textTransform: 'uppercase', letterSpacing: 0.5 }}>
              Client <span style={{ color: 'var(--rouge)' }}>*</span>
            </label>
            {window.ClientPicker ? (
              <window.ClientPicker value={client} onChange={setClient} placeholder="Rechercher client Odoo / AE / Organisation…" autoFocus required />
            ) : (
              <div style={{ fontSize: 11, color: 'var(--rouge)' }}>ClientPicker non chargé</div>
            )}
          </div>

          {/* Type de capture */}
          <div>
            <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--ink-3)', marginBottom: 5, textTransform: 'uppercase', letterSpacing: 0.5 }}>
              Type de capture
            </label>
            <div style={{ display: 'flex', gap: 6 }}>
              {[
                { val: 'video', label: 'Vidéo + audio', desc: 'Filmer + capter la voix' },
                { val: 'audio', label: 'Audio seul', desc: 'Synthèse RDV' },
                { val: 'mixed', label: 'Mixte', desc: 'Vidéo + photos + audio' },
              ].map(o => (
                <button key={o.val} onClick={() => setType(o.val)} style={{
                  flex: 1, padding: '8px 10px', fontSize: 11,
                  background: type === o.val ? 'var(--signal-tint)' : 'var(--paper-2)',
                  color: type === o.val ? 'var(--signal-deep)' : 'var(--ink-3)',
                  border: '1px solid ' + (type === o.val ? 'var(--signal)' : 'var(--line-2)'),
                  borderRadius: 4, cursor: 'pointer', fontWeight: type === o.val ? 600 : 500, textAlign: 'left',
                }}>
                  <div>{o.label}</div>
                  <div style={{ fontSize: 9, color: 'var(--ink-4)', marginTop: 2 }}>{o.desc}</div>
                </button>
              ))}
            </div>
          </div>

          {/* Adresse site */}
          <div>
            <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--ink-3)', marginBottom: 5, textTransform: 'uppercase', letterSpacing: 0.5 }}>
              Adresse du site
            </label>
            <input value={address} onChange={e => setAddress(e.target.value)} placeholder="Adresse complète (pré-remplie depuis le client)" style={{
              width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)',
              borderRadius: 4, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit',
            }} />
            <div style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 3 }}>
              Sera utilisée pour l'analyse satellite (BAN → IGN → PVGIS).
            </div>
          </div>

          {/* Notes */}
          <div>
            <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--ink-3)', marginBottom: 5, textTransform: 'uppercase', letterSpacing: 0.5 }}>
              Notes (optionnel)
            </label>
            <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2} placeholder="Contexte de la visite, points à vérifier…" style={{
              width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)',
              borderRadius: 4, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit', resize: 'vertical',
            }} />
          </div>

          {error && (
            <div style={{ padding: '8px 10px', fontSize: 11, background: 'var(--rouge-tint)', color: 'var(--rouge-deep)', borderRadius: 4 }}>
              {error}
            </div>
          )}
        </div>

        <div style={{ padding: '12px 18px', borderTop: '1px solid var(--hairline)', display: 'flex', gap: 8, justifyContent: 'flex-end', background: 'var(--paper-2)' }}>
          <Btn variant="ghost" size="sm" onClick={onClose} disabled={saving}>Annuler</Btn>
          <Btn variant="signal" size="sm" onClick={submit} disabled={!canSubmit} iconRight={<Icon.arrow />}>
            {saving ? 'Création…' : 'Créer la visite'}
          </Btn>
        </div>
      </div>
    </div>
  );
}

// ─── VisiteSettingsButton — Personnalisation Visites (Règle n°6) ────────────
function VisiteSettingsButton() {
  const [open, setOpen] = _vtUseS(false);
  return (
    <>
      <button onClick={() => setOpen(true)} title="Personnalisation Visites" style={{
        padding: '6px 10px', fontSize: 12, background: 'var(--paper)',
        border: '1px solid var(--line-2)', borderRadius: 5, cursor: 'pointer', color: 'var(--ink-3)',
      }}>⚙️ Paramètres</button>
      {open && <VisiteSettingsModal onClose={() => setOpen(false)} />}
    </>
  );
}

function VisiteSettingsModal({ onClose }) {
  const KEY = 'ae_visite_settings';
  const defaults = () => ({
    visionAuto: true,
    visionModel: 'claude-sonnet-4-5',
    audioTranscriptAuto: true,
    audioModel: 'whisper-1',
    confidenceThreshold: 0.7,
    pushToCrmAuto: true,
    autoCreateOpportunity: false,
    deleteRawMediaAfterDays: 30,
    networkOfflineMode: 'queue',  // queue | block
    photoQuality: 'high',          // low | medium | high
    audioQuality: 'medium',
    fostAutoMatch: true,
    notifyTechnicianOnAnalysis: true,
  });
  const [s, setS] = _vtUseS(() => {
    try { return { ...defaults(), ...(JSON.parse(localStorage.getItem(KEY)) || {}) }; }
    catch { return defaults(); }
  });
  const [saved, setSaved] = _vtUseS(false);
  const upd = (k, v) => setS(p => ({ ...p, [k]: v }));
  const save = () => { localStorage.setItem(KEY, JSON.stringify(s)); setSaved(true); setTimeout(() => setSaved(false), 2000); };
  const reset = () => { if (confirm('Réinitialiser tous les paramètres ?')) setS(defaults()); };
  const inp = { width: '100%', padding: '6px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 4, background: 'var(--paper-2)', color: 'var(--ink)', boxSizing: 'border-box' };
  const lbl = { fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '.04em', fontWeight: 600, marginBottom: 4 };

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.5)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: 'var(--paper)', borderRadius: 10, maxWidth: 700, width: '100%', maxHeight: '90vh', overflowY: 'auto', boxShadow: '0 8px 40px rgba(0,0,0,.3)' }}>
        <div style={{ padding: '14px 20px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ fontSize: 16, fontWeight: 700, flex: 1 }}>⚙️ Personnalisation Visites</div>
          <button onClick={onClose} style={{ padding: 4, fontSize: 16 }}>×</button>
        </div>
        <div style={{ padding: 20, display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '.05em', fontWeight: 700, marginBottom: 8 }}>IA Vision (équipements/plaques)</div>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 6 }}>
              <input type="checkbox" checked={s.visionAuto} onChange={e => upd('visionAuto', e.target.checked)} />
              Analyse Claude Vision automatique sur chaque photo
            </label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <div><div style={lbl}>Modèle Vision</div>
                <select value={s.visionModel} onChange={e => upd('visionModel', e.target.value)} style={inp}>
                  <option value="claude-sonnet-4-5">Claude Sonnet 4.5</option>
                  <option value="claude-opus-4-5">Claude Opus 4.5</option>
                </select>
              </div>
              <div><div style={lbl}>Seuil confiance min</div><input type="number" step="0.1" min="0" max="1" value={s.confidenceThreshold} onChange={e => upd('confidenceThreshold', parseFloat(e.target.value) || 0.7)} style={inp}/></div>
            </div>
          </div>
          <div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '.05em', fontWeight: 700, marginBottom: 8 }}>IA Audio (transcription/NLP)</div>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 6 }}>
              <input type="checkbox" checked={s.audioTranscriptAuto} onChange={e => upd('audioTranscriptAuto', e.target.checked)} />
              Transcription audio automatique
            </label>
            <div><div style={lbl}>Modèle audio</div>
              <select value={s.audioModel} onChange={e => upd('audioModel', e.target.value)} style={inp}>
                <option value="whisper-1">OpenAI Whisper</option>
                <option value="claude-audio">Claude Audio NLP</option>
                <option value="deepgram">Deepgram</option>
              </select>
            </div>
          </div>
          <div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '.05em', fontWeight: 700, marginBottom: 8 }}>Workflow CRM</div>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 6 }}>
              <input type="checkbox" checked={s.pushToCrmAuto} onChange={e => upd('pushToCrmAuto', e.target.checked)} />
              Push automatique vers CRM après analyse
            </label>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 6 }}>
              <input type="checkbox" checked={s.autoCreateOpportunity} onChange={e => upd('autoCreateOpportunity', e.target.checked)} />
              Créer une opportunité Odoo automatiquement
            </label>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 6 }}>
              <input type="checkbox" checked={s.fostAutoMatch} onChange={e => upd('fostAutoMatch', e.target.checked)} />
              Matcher automatiquement les FOST candidats
            </label>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
              <input type="checkbox" checked={s.notifyTechnicianOnAnalysis} onChange={e => upd('notifyTechnicianOnAnalysis', e.target.checked)} />
              Notifier le technicien à la fin de l'analyse
            </label>
          </div>
          <div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '.05em', fontWeight: 700, marginBottom: 8 }}>Stockage médias</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
              <div><div style={lbl}>Qualité photo</div>
                <select value={s.photoQuality} onChange={e => upd('photoQuality', e.target.value)} style={inp}>
                  <option value="low">Basse (compressé)</option><option value="medium">Moyenne</option><option value="high">Haute (originale)</option>
                </select>
              </div>
              <div><div style={lbl}>Qualité audio</div>
                <select value={s.audioQuality} onChange={e => upd('audioQuality', e.target.value)} style={inp}>
                  <option value="low">Basse (8kHz mono)</option><option value="medium">Moyenne (16kHz)</option><option value="high">Haute (44kHz stereo)</option>
                </select>
              </div>
              <div><div style={lbl}>Suppression brut après (j)</div><input type="number" min={0} max={365} value={s.deleteRawMediaAfterDays} onChange={e => upd('deleteRawMediaAfterDays', parseInt(e.target.value) || 30)} style={inp}/></div>
            </div>
            <div style={{ marginTop: 10 }}>
              <div style={lbl}>Mode hors-ligne</div>
              <select value={s.networkOfflineMode} onChange={e => upd('networkOfflineMode', e.target.value)} style={inp}>
                <option value="queue">Queue locale (sync au retour réseau)</option>
                <option value="block">Bloquer (refuser captures hors-ligne)</option>
              </select>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', paddingTop: 12, borderTop: '1px solid var(--hairline)' }}>
            <button onClick={reset} style={{ padding: '8px 14px', fontSize: 12, color: 'var(--ink-3)', background: 'var(--paper)', border: '1px solid var(--line-2)', borderRadius: 5, cursor: 'pointer' }}>↺ Réinitialiser</button>
            <button onClick={save} style={{ padding: '8px 18px', fontSize: 12, fontWeight: 600, color: 'var(--paper)', background: saved ? 'var(--signal-deep)' : 'var(--ink)', border: 'none', borderRadius: 5, cursor: 'pointer' }}>
              {saved ? '✓ Enregistré' : 'Enregistrer'}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { VisitesPage, NewVisiteModal, VT_STATUS_META, VisiteSettingsButton, VisiteSettingsModal });
