/* ============================================================================
   Vocal · Bot Studio — bots.vocal.ch
   Console dédiée à la conception, l'entraînement, le test et la publication
   des bots vocaux IA. Backend partagé : api.vocal.ch (worker ch-vocal-api).
   ============================================================================ */

const { useState, useEffect, useRef, useCallback, useMemo, createContext, useContext, Fragment } = React;

const CONFIG = {
    API_URL: 'https://api.vocal.ch',
    MY_URL: 'https://my.vocal.ch',
    DEV_URL: 'https://developers.vocal.ch',
};

// ==================== ADMIN IMPERSONATION + CROSS-SUBDOMAIN SSO ====================
// Si on arrive depuis my.vocal.ch ou adm.vocal.ch avec un token, on l'adopte.
(() => {
    try {
        const params = new URLSearchParams(window.location.search);
        const tok = params.get('impersonate_token') || params.get('token');
        if (tok) {
            localStorage.setItem('vocal_my_token', tok);
            const userB64 = params.get('impersonate_user') || params.get('user');
            if (userB64) {
                try {
                    const u = JSON.parse(decodeURIComponent(escape(atob(userB64))));
                    localStorage.setItem('vocal_my_user', JSON.stringify(u));
                } catch (e) {}
            }
            sessionStorage.setItem('vocal_my_impersonating', '1');
            const clean = window.location.pathname + window.location.hash;
            window.history.replaceState(null, '', clean);
        }
    } catch (e) {}
})();

// ==================== CONSTANTS ====================
const LANGS = [
    { id: 'fr', label: 'Français' },
    { id: 'de', label: 'Deutsch' },
    { id: 'en', label: 'English' },
    { id: 'it', label: 'Italiano' },
];
const VOICES = [
    { id: 'alloy',   label: 'Alloy — neutre & polyvalent', provider: 'openai' },
    { id: 'echo',    label: 'Echo — masculin chaleureux',  provider: 'openai' },
    { id: 'fable',   label: 'Fable — narratif',            provider: 'openai' },
    { id: 'onyx',    label: 'Onyx — masculin grave',       provider: 'openai' },
    { id: 'nova',    label: 'Nova — féminin énergique',    provider: 'openai' },
    { id: 'shimmer', label: 'Shimmer — féminin doux',      provider: 'openai' },
];

const SECTOR_TEMPLATES = [
    {
        id: 'restaurant', iconKey: 'Utensils', kind: 'resto',
        title: 'Restaurant',
        desc: 'Réservations, horaires, menu du jour, allergènes, take-away.',
        features: ['Réservations', 'Carte du jour', 'Allergènes', 'Take-away'],
        defaults: {
            name: 'Bot Restaurant',
            description: 'Assistant vocal pour mon restaurant',
            language: 'fr',
            voice: 'nova',
            greeting: 'Bonjour, restaurant {NOM}, j\'écoute !',
            instructions: 'Tu es l\'hôte vocal du restaurant {NOM} situé à {VILLE}. Tu prends les réservations (date, heure, nombre de personnes, nom). Tu peux annoncer les horaires d\'ouverture, le menu du jour, les allergènes courants. Tu es chaleureux, concis. Si la demande sort de ton périmètre, tu transfères au {TRANSFERT}.',
            appointments_enabled: true,
            appointment_duration_minutes: 90,
        },
    },
    {
        id: 'medical', iconKey: 'Stethoscope', kind: 'medical',
        title: 'Cabinet médical / dentaire',
        desc: 'Prise de rendez-vous, urgences, ordonnances, instructions pré-RDV.',
        features: ['Prise de RDV', 'Urgences', 'Triage léger', 'FAQ patient'],
        defaults: {
            name: 'Bot Cabinet médical',
            description: 'Assistant vocal pour mon cabinet médical',
            language: 'fr',
            voice: 'shimmer',
            greeting: 'Cabinet du Dr {NOM}, bonjour. Je suis l\'assistant vocal, comment puis-je vous aider ?',
            instructions: 'Tu es l\'assistant vocal du cabinet médical du Dr {NOM}. Tu prends les rendez-vous, indiques les horaires d\'ouverture, donnes les consignes de préparation pour les examens. Tu NE donnes JAMAIS de conseil médical. En cas d\'urgence vitale, tu rediriges immédiatement vers le 144. Pour toute question médicale, tu transfères au {TRANSFERT} ou tu indiques de rappeler aux heures d\'ouverture.',
            appointments_enabled: true,
            appointment_duration_minutes: 20,
        },
    },
    {
        id: 'legal', iconKey: 'Gavel', kind: 'legal',
        title: 'Cabinet d\'avocat / fiduciaire',
        desc: 'Premier contact qualifié, qualification BANT, prise de rendez-vous.',
        features: ['Qualification client', 'Lead scoring', 'RDV consultation', 'FAQ honoraires'],
        defaults: {
            name: 'Bot Cabinet juridique',
            description: 'Assistant vocal pour cabinet d\'avocat / fiduciaire',
            language: 'fr',
            voice: 'echo',
            greeting: 'Étude {NOM}, bonjour. Je suis l\'assistant de premier contact.',
            instructions: 'Tu es l\'assistant vocal de l\'étude {NOM}. Tu qualifies les nouveaux dossiers (type de litige/dossier, urgence, budget approximatif), tu donnes les horaires, tu fixes une consultation initiale (45 min). Tu NE donnes JAMAIS de conseil juridique au téléphone. Tu transfères au {TRANSFERT} les dossiers urgents ou complexes.',
            appointments_enabled: true,
            appointment_duration_minutes: 45,
            lead_scoring_enabled: true,
        },
    },
    {
        id: 'garage', iconKey: 'Wrench', kind: 'garage',
        title: 'Garage / atelier auto',
        desc: 'Prise de RDV service, devis pneus, rappel CT, urgences dépannage.',
        features: ['RDV service', 'Devis express', 'Rappel CT', 'Dépannage'],
        defaults: {
            name: 'Bot Garage',
            description: 'Assistant vocal pour mon garage',
            language: 'fr',
            voice: 'onyx',
            greeting: 'Garage {NOM}, bonjour, comment puis-je vous aider ?',
            instructions: 'Tu es l\'assistant du garage {NOM}. Tu prends des rendez-vous service (vidange, pneus, contrôle technique, diagnostic), tu donnes les horaires, les marques travaillées et les délais habituels. Pour un dépannage ou un véhicule en panne, tu transfères immédiatement au {TRANSFERT}. Pour les devis précis, tu prends les coordonnées et fais rappeler.',
            appointments_enabled: true,
            appointment_duration_minutes: 30,
        },
    },
    {
        id: 'ecom', iconKey: 'Cart', kind: 'ecom',
        title: 'E-commerce',
        desc: 'Suivi de commande, retours, FAQ produit, intégration Shopify.',
        features: ['Suivi commande', 'Politique retour', 'FAQ produit', 'API Shopify'],
        defaults: {
            name: 'Bot E-commerce',
            description: 'Assistant vocal pour ma boutique en ligne',
            language: 'fr',
            voice: 'nova',
            greeting: 'Boutique {NOM}, bonjour ! Comment puis-je vous aider avec votre commande ?',
            instructions: 'Tu es l\'assistant vocal de la boutique {NOM}. Tu peux répondre sur le statut d\'une commande (demande le n°), expliquer la politique de retour (14 jours), donner les délais de livraison, répondre aux questions produit en t\'appuyant sur la base de connaissance. Pour les SAV complexes ou réclamations, tu transfères au {TRANSFERT}.',
            appointments_enabled: false,
        },
    },
    {
        id: 'beauty', iconKey: 'Scissors', kind: 'beauty',
        title: 'Salon de coiffure / beauté',
        desc: 'Prise de RDV, prestations, prix, conseils produits.',
        features: ['RDV multi-prestation', 'Tarifs', 'Confirmation SMS', 'Annulation'],
        defaults: {
            name: 'Bot Salon',
            description: 'Assistant vocal pour mon salon de beauté',
            language: 'fr',
            voice: 'shimmer',
            greeting: 'Salon {NOM}, bonjour ! Souhaitez-vous prendre rendez-vous ?',
            instructions: 'Tu es l\'assistante du salon {NOM}. Tu prends les rendez-vous en demandant la prestation (coupe, couleur, brushing, soin…), la coiffeuse préférée, la date et l\'heure. Tu donnes les tarifs courants et les durées approximatives. Tu confirmes par SMS.',
            appointments_enabled: true,
            appointment_duration_minutes: 60,
        },
    },
    {
        id: 'blank', iconKey: 'Sparkles', kind: 'other',
        title: 'Bot vierge',
        desc: 'Partez d\'une page blanche pour un cas d\'usage spécifique.',
        features: ['Configuration libre', 'Sources URL', 'KB Q&A', 'Voix premium'],
        defaults: {
            name: 'Mon bot',
            description: '',
            language: 'fr',
            voice: 'alloy',
            greeting: 'Bonjour, je vous écoute.',
            instructions: 'Tu es un assistant vocal professionnel. Tu réponds de manière concise et claire. Si tu ne sais pas répondre, tu proposes de rappeler le client.',
            appointments_enabled: false,
        },
    },
];

// Voix de démonstration pour la marketplace (à remplacer par GET /marketplace/voices côté API).
const MARKETPLACE_VOICES_DEMO = [
    { id: 'mkt_1', name: 'Sophie · Romande', creator: 'Studio Léman', desc: 'Voix féminine douce, accent romand, idéale médical/beauté.', tags: ['FR-CH', 'féminin', 'doux'], price_chf: 19, calls_used: 1240 },
    { id: 'mkt_2', name: 'Markus · Bern', creator: 'Voiceworks GmbH', desc: 'Voix masculine grave, Schwizerdütsch berniñois, garage/PME.', tags: ['DE-CH', 'masculin', 'grave'], price_chf: 24, calls_used: 870 },
    { id: 'mkt_3', name: 'Luca · Ticino', creator: 'Studio Tre', desc: 'Voix masculine chaleureuse, italien tessinois, restaurant/hôtel.', tags: ['IT-CH', 'masculin', 'chaud'], price_chf: 22, calls_used: 540 },
    { id: 'mkt_4', name: 'Emma · Geneva', creator: 'Lac Voice', desc: 'Voix féminine premium, accent genevois pro, juridique/finance.', tags: ['FR-CH', 'féminin', 'pro'], price_chf: 29, calls_used: 2110 },
    { id: 'mkt_5', name: 'Hans · Zürich', creator: 'Voiceworks GmbH', desc: 'Voix masculine corporate, Schwizerdütsch zurichois, B2B.', tags: ['DE-CH', 'masculin', 'corporate'], price_chf: 26, calls_used: 1620 },
    { id: 'mkt_6', name: 'Léa · Vaud', creator: 'Vocal Originals', desc: 'Voix féminine jeune et dynamique, parfait e-commerce romand.', tags: ['FR-CH', 'féminin', 'jeune'], price_chf: 21, calls_used: 980 },
];

// ==================== FORMATTERS ====================
function fmtDate(d) { if (!d) return '-'; return new Date(d).toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', year: 'numeric' }); }
function fmtDateTime(d) { if (!d) return '-'; const x = new Date(d); return `${String(x.getDate()).padStart(2,'0')}/${String(x.getMonth()+1).padStart(2,'0')}/${x.getFullYear()} ${String(x.getHours()).padStart(2,'0')}:${String(x.getMinutes()).padStart(2,'0')}`; }
function fmtDuration(s) { if (!s) return '-'; const m = Math.floor(s/60); const sec = s%60; if (m === 0) return `${sec}s`; return `${m}m${sec>0?String(sec).padStart(2,'0')+'s':''}`; }
function fmtCurrency(amount, currency = 'CHF') { if (amount === null || amount === undefined) return '-'; return `${Number(amount).toFixed(2)} ${currency}`; }
function fmtRelative(d) {
    if (!d) return '-';
    const diff = (Date.now() - new Date(d).getTime()) / 1000;
    if (diff < 60) return 'à l\'instant';
    if (diff < 3600) return `il y a ${Math.floor(diff / 60)} min`;
    if (diff < 86400) return `il y a ${Math.floor(diff / 3600)} h`;
    if (diff < 604800) return `il y a ${Math.floor(diff / 86400)} j`;
    return fmtDate(d);
}

// ==================== ICONS (subset) ====================
const Icon = (path, viewBox = '0 0 24 24') => ({ className = 'w-5 h-5' }) => (
    <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox={viewBox} stroke="currentColor" strokeWidth="2">
        {path}
    </svg>
);
const Icons = {
    Robot: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M12 2v3m-6 3h12a2 2 0 012 2v8a2 2 0 01-2 2H6a2 2 0 01-2-2v-8a2 2 0 012-2zm3 4h6m-6 4h.01m5.99 0H15M9 18h6"/>),
    Home: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>),
    Settings: Icon(<><path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></>),
    Sparkles: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"/>),
    Mic: Icon(<><path strokeLinecap="round" strokeLinejoin="round" d="M19 11a7 7 0 01-14 0m7 7v3m-4 0h8M12 4a3 3 0 00-3 3v4a3 3 0 006 0V7a3 3 0 00-3-3z"/></>),
    Globe: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>),
    Document: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>),
    Brain: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/>),
    Database: Icon(<><path strokeLinecap="round" strokeLinejoin="round" d="M4 7v10c0 2 4 3 8 3s8-1 8-3V7"/><path strokeLinecap="round" strokeLinejoin="round" d="M4 12c0 2 4 3 8 3s8-1 8-3"/><ellipse cx="12" cy="6" rx="8" ry="3" fill="none" stroke="currentColor"/></>),
    Play: Icon(<><path strokeLinecap="round" strokeLinejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path strokeLinecap="round" strokeLinejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></>),
    Beaker: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 9a4 4 0 01-8 0L8 4z"/>),
    Phone: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>),
    Calendar: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>),
    AlertCircle: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>),
    Clock: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>),
    Chart: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>),
    Plus: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4"/>),
    ChevronLeft: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7"/>),
    ChevronRight: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7"/>),
    ChevronDown: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>),
    Trash: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>),
    Edit: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>),
    Send: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>),
    PaperPlane: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>),
    Refresh: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>),
    Menu: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16"/>),
    LogOut: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>),
    Check: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/>),
    X: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/>),
    Upload: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>),
    Star: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/>),
    Lightning: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/>),
    Branch: Icon(<><path strokeLinecap="round" strokeLinejoin="round" d="M6 3v12"/><circle cx="6" cy="6" r="2.5" fill="none"/><circle cx="6" cy="18" r="2.5" fill="none"/><circle cx="18" cy="6" r="2.5" fill="none"/><path strokeLinecap="round" strokeLinejoin="round" d="M18 8.5c0 4-6 4-6 8.5"/></>),
    External: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>),
    Search: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>),
    Phone2: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>),
    Headset: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 14a9 9 0 0118 0v3a2 2 0 01-2 2h-1v-5h3M3 14v3a2 2 0 002 2h1v-5H3"/>),
    Bolt: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/>),
    Layers: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>),
    Utensils: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M4 3v6a2 2 0 002 2h0a2 2 0 002-2V3M6 11v10M14 3v18M14 9c0-3.314 1.79-6 4-6v18"/>),
    Stethoscope: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M5 3v6a4 4 0 008 0V3M9 13v4a4 4 0 008 0v-2m0 0a2 2 0 100-4 2 2 0 000 4z"/>),
    Gavel: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M14 3l7 7-3 3-7-7 3-3zM5 21h10M9 13l-5 5 3 3 5-5"/>),
    Wrench: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M14.7 6.3a4 4 0 00-5.4 5.4L3 18l3 3 6.3-6.3a4 4 0 005.4-5.4l-2.5 2.5-2.5-2.5 2.5-2.5z"/>),
    Cart: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/>),
    Scissors: Icon(<><circle cx="6" cy="6" r="3" fill="none"/><circle cx="6" cy="18" r="3" fill="none"/><path strokeLinecap="round" strokeLinejoin="round" d="M9 6l12 12M21 6L9 18"/></>),
    Lightbulb: Icon(<path strokeLinecap="round" strokeLinejoin="round" d="M9 18h6m-5 3h4m-7-9a7 7 0 1112-4.9A7 7 0 0114 14v3H10v-3a7 7 0 01-3-2"/>),
};

// ==================== API ====================
const api = {
    token: null,
    async req(path, options = {}) {
        const headers = { 'Content-Type': 'application/json', ...options.headers };
        if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
        try {
            const resp = await fetch(`${CONFIG.API_URL}${path}`, { ...options, headers });
            const data = await resp.json().catch(() => ({}));
            if (!resp.ok && !data.error) data.error = `HTTP ${resp.status}`;
            data._status = resp.status;
            return data;
        } catch (e) {
            return { error: 'Erreur réseau', _status: 0 };
        }
    },
    get(path) { return this.req(path); },
    post(path, body) { return this.req(path, { method: 'POST', body: JSON.stringify(body || {}) }); },
    put(path, body) { return this.req(path, { method: 'PUT', body: JSON.stringify(body || {}) }); },
    del(path) { return this.req(path, { method: 'DELETE' }); },
};

// ==================== NOTIFICATIONS ====================
const useNotifications = () => {
    const show = useCallback((message, type = 'info') => {
        const el = document.createElement('div');
        el.style.cssText = 'position:fixed;bottom:1.5rem;right:1.5rem;z-index:9999;padding:0.85rem 1.2rem;border-radius:12px;font-size:0.85rem;font-weight:600;color:#fff;box-shadow:0 12px 32px rgba(15,23,42,0.18);max-width:360px;font-family:Plus Jakarta Sans,sans-serif;animation:fadeIn 0.2s ease;';
        const colors = { success: '#16a34a', error: '#dc2626', info: '#6366f1', warning: '#ea580c' };
        el.style.background = colors[type] || colors.info;
        el.textContent = message;
        document.body.appendChild(el);
        setTimeout(() => { el.style.opacity = '0'; el.style.transform = 'translateY(8px)'; el.style.transition = 'all 0.25s'; setTimeout(() => el.remove(), 280); }, 3500);
    }, []);
    return useMemo(() => ({
        success: (m) => show(m, 'success'),
        error: (m) => show(m, 'error'),
        info: (m) => show(m, 'info'),
        warning: (m) => show(m, 'warning'),
    }), [show]);
};

// ==================== ROUTING ====================
function parseHash() {
    const h = (window.location.hash || '').replace('#', '');
    if (!h) return { view: 'bots' };
    if (h.startsWith('bot-')) {
        const parts = h.slice(4).split('/');
        const botId = parseInt(parts[0]);
        if (!Number.isFinite(botId)) return { view: 'bots' };
        return { view: 'bot-detail', botId, panel: parts[1] || 'overview' };
    }
    const tops = ['bots', 'templates', 'marketplace', 'analytics', 'account'];
    return { view: tops.includes(h) ? h : 'bots' };
}
function buildHash(view, opts = {}) {
    if (view === 'bot-detail') return `bot-${opts.botId}/${opts.panel || 'overview'}`;
    return view;
}

// ==================== CONTEXT ====================
const AppContext = createContext();
const useApp = () => useContext(AppContext);

const AppProvider = ({ children }) => {
    const [user, setUser] = useState(() => {
        const saved = localStorage.getItem('vocal_my_user');
        const token = localStorage.getItem('vocal_my_token');
        if (saved && token) { api.token = token; try { return { ...JSON.parse(saved), token }; } catch { return null; } }
        return null;
    });
    const [route, setRoute] = useState(parseHash);
    const [sidebarOpen, setSidebarOpen] = useState(false);
    const notify = useNotifications();

    useEffect(() => {
        const onHash = () => setRoute(parseHash());
        window.addEventListener('hashchange', onHash);
        return () => window.removeEventListener('hashchange', onHash);
    }, []);

    const navigate = useCallback((view, opts = {}) => {
        const next = view === 'bot-detail' ? { view, botId: opts.botId, panel: opts.panel || 'overview' } : { view };
        setRoute(next);
        setSidebarOpen(false);
        try { window.history.pushState(null, '', `#${buildHash(view, opts)}`); } catch (e) {}
    }, []);

    const login = useCallback((userData, token) => {
        api.token = token;
        setUser({ ...userData, token });
        localStorage.setItem('vocal_my_user', JSON.stringify(userData));
        localStorage.setItem('vocal_my_token', token);
        notify.success('Connexion réussie');
    }, [notify]);

    const logout = useCallback(() => {
        api.post('/auth/logout').catch(() => {});
        api.token = null;
        setUser(null);
        localStorage.removeItem('vocal_my_user');
        localStorage.removeItem('vocal_my_token');
        notify.info('Déconnexion réussie');
    }, [notify]);

    const value = useMemo(() => ({
        user, setUser, route, navigate, login, logout, sidebarOpen, setSidebarOpen, notify,
    }), [user, route, navigate, login, logout, sidebarOpen, notify]);

    return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

// ==================== LOGIN ====================
const LoginScreen = () => {
    const { login } = useApp();
    const [email, setEmail] = useState('');
    const [code, setCode] = useState('');
    const [step, setStep] = useState('email');
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState('');

    useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const magicEmail = params.get('email');
        const magicCode = params.get('code');
        if (magicEmail && magicCode) {
            window.history.replaceState({}, '', window.location.pathname);
            setLoading(true);
            fetch(`${CONFIG.API_URL}/auth/verify-code`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email: magicEmail, code: magicCode }),
            }).then(r => r.json()).then(data => {
                if (data.success && data.token) login(data.user || { email: magicEmail }, data.token);
                else { setEmail(magicEmail); setStep('code'); setError(data.error || 'Lien expiré'); }
            }).catch(() => { setEmail(magicEmail); setStep('code'); setError('Erreur réseau'); })
              .finally(() => setLoading(false));
        }
    }, []);

    const handleSendCode = async (e) => {
        e.preventDefault(); if (!email) return;
        setLoading(true); setError('');
        try {
            const r = await fetch(`${CONFIG.API_URL}/auth/request-code`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }),
            }).then(r => r.json());
            if (r.success) setStep('code');
            else setError(r.error || 'Erreur');
        } catch (e) { setError('Erreur réseau'); }
        finally { setLoading(false); }
    };
    const handleVerify = async (e) => {
        e.preventDefault(); if (!code) return;
        setLoading(true); setError('');
        try {
            const r = await fetch(`${CONFIG.API_URL}/auth/verify-code`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, code }),
            }).then(r => r.json());
            if (r.success && r.token) login(r.user || { email }, r.token);
            else setError(r.error || 'Code invalide');
        } catch (e) { setError('Erreur réseau'); }
        finally { setLoading(false); }
    };

    return (
        <div className="login-split">
            <div className="login-form-side">
                <div className="login-form-inner">
                    <div className="login-brand">
                        <img src="assets/img/vocal-logotype.svg" alt="Vocal" />
                        <span className="pill">Bot Studio</span>
                    </div>
                    <h1 className="login-title">{step === 'email' ? 'Concevez vos bots vocaux' : 'Vérifiez votre email'}</h1>
                    <p className="login-subtitle">
                        {step === 'email'
                            ? 'Recevez un code de connexion sécurisé à usage unique. Aucun mot de passe à gérer.'
                            : `Un code à 6 chiffres a été envoyé à ${email}.`}
                    </p>
                    {error && <div className="login-error">{error}</div>}
                    {step === 'email' ? (
                        <form onSubmit={handleSendCode} className="vstack gap-lg">
                            <div className="form-row">
                                <label>Email</label>
                                <input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="vous@entreprise.ch" required autoFocus />
                            </div>
                            <button type="submit" className="btn btn-primary" disabled={loading || !email} style={{ width: '100%' }}>
                                {loading ? 'Envoi…' : 'Recevoir le code'}
                            </button>
                            <div className="text-xs text-muted" style={{ textAlign: 'center' }}>
                                Vous avez déjà un compte sur <a href={CONFIG.MY_URL}>my.vocal.ch</a> ? Utilisez la même adresse.
                            </div>
                        </form>
                    ) : (
                        <form onSubmit={handleVerify} className="vstack gap-lg">
                            <div className="form-row">
                                <label>Code reçu par email</label>
                                <input type="text" inputMode="numeric" maxLength={6} value={code} onChange={e => setCode(e.target.value.replace(/\D/g, ''))} placeholder="123456" required autoFocus style={{ letterSpacing: '0.6em', textAlign: 'center', fontSize: '1.25rem', fontWeight: 700 }} />
                            </div>
                            <button type="submit" className="btn btn-primary" disabled={loading || code.length < 4} style={{ width: '100%' }}>
                                {loading ? 'Connexion…' : 'Se connecter'}
                            </button>
                            <button type="button" className="btn btn-ghost btn-sm" onClick={() => { setStep('email'); setCode(''); setError(''); }}>← Changer d'email</button>
                        </form>
                    )}
                </div>
            </div>
            <aside className="login-aside">
                <div className="login-aside-inner">
                    <h2>Le studio dédié à vos bots vocaux IA</h2>
                    <p>Concevez, entraînez sur vos données, testez en live et publiez sur vos lignes téléphoniques en quelques clics.</p>
                    <div className="feature-mini-grid">
                        <div className="feature-mini"><div className="ico"><Icons.Brain className="w-5 h-5" /></div><div className="t">Apprentissage auto</div><div className="d">Le bot s'améliore après chaque appel</div></div>
                        <div className="feature-mini"><div className="ico"><Icons.Mic className="w-5 h-5" /></div><div className="t">Voix premium</div><div className="d">OpenAI, ElevenLabs, marketplace CH</div></div>
                        <div className="feature-mini"><div className="ico"><Icons.Beaker className="w-5 h-5" /></div><div className="t">Simulator live</div><div className="d">Testez avant de publier</div></div>
                        <div className="feature-mini"><div className="ico"><Icons.Branch className="w-5 h-5" /></div><div className="t">A/B testing</div><div className="d">Trouvez la version qui convertit</div></div>
                        <div className="feature-mini"><div className="ico"><Icons.Document className="w-5 h-5" /></div><div className="t">Sources URL/PDF</div><div className="d">Importez votre site, vos PDF</div></div>
                        <div className="feature-mini"><div className="ico"><Icons.Globe className="w-5 h-5" /></div><div className="t">Hébergement Suisse</div><div className="d">Cloudflare CH, RGPD/nLPD</div></div>
                    </div>
                </div>
            </aside>
        </div>
    );
};

// ==================== SHARED COMPONENTS ====================
const PageHeader = ({ title, subtitle, actions, breadcrumb }) => {
    const { setSidebarOpen } = useApp();
    return (
        <div className="page-header">
            <div className="page-header-left">
                <button className="menu-toggle" onClick={() => setSidebarOpen(true)} aria-label="Menu">
                    <Icons.Menu />
                </button>
                <div>
                    {breadcrumb && <div className="breadcrumb">{breadcrumb}</div>}
                    <h1 className="page-title">{title}</h1>
                    {subtitle && <p className="page-subtitle">{subtitle}</p>}
                </div>
            </div>
            {actions && <div className="page-actions">{actions}</div>}
        </div>
    );
};

const SoonBanner = ({ endpoint, children }) => (
    <div className="soon-banner">
        <Icons.Lightning className="w-5 h-5" />
        <div>
            <b>Aperçu produit.</b> {children || 'Cette section affiche l\'UX cible. '}
            {endpoint && <>Pour activer le mode live, branchez l'endpoint <code>{endpoint}</code> côté <code>ch-vocal-api</code>.</>}
            {' '}<a href="https://github.com/" onClick={(e) => { e.preventDefault(); window.open('https://github.com/', '_blank'); }} style={{ color: '#713f12', textDecoration: 'underline' }}>Voir spec V2_FEATURES.md</a>
        </div>
    </div>
);

const EmptyState = ({ icon: I = Icons.Sparkles, title, desc, action }) => (
    <div className="empty-state">
        <div className="empty-state-icon"><I className="w-8 h-8" /></div>
        <h3 className="empty-state-title">{title}</h3>
        {desc && <p className="empty-state-desc">{desc}</p>}
        {action}
    </div>
);

const Spinner = ({ size = 24 }) => (
    <span style={{ display: 'inline-block', width: size, height: size, border: '3px solid var(--border)', borderTopColor: 'var(--primary)', borderRadius: '50%', animation: 'spin 0.75s linear infinite' }} />
);

const StatusDot = ({ status }) => {
    const cls = status === 'active' ? 'live' : status === 'draft' ? 'draft' : 'paused';
    const label = status === 'active' ? 'En production' : status === 'draft' ? 'Brouillon' : 'En pause';
    return <><span className={`dot ${cls}`} />{label}</>;
};

// ==================== SIDEBAR ====================
const TOP_LEVEL_NAV = [
    { id: 'bots',        label: 'Mes bots',           icon: Icons.Robot,     section: 'studio' },
    { id: 'templates',   label: 'Templates par secteur', icon: Icons.Layers, section: 'studio' },
    { id: 'marketplace', label: 'Marketplace voix',   icon: Icons.Mic,       section: 'studio' },
    { id: 'analytics',   label: 'Analytics globales', icon: Icons.Chart,     section: 'compte' },
    { id: 'account',     label: 'Réglages compte',    icon: Icons.Settings,  section: 'compte' },
];
const TOP_LEVEL_SECTIONS = { studio: 'BOT STUDIO', compte: 'COMPTE' };

const BOT_NAV = [
    { id: 'overview',    label: 'Vue d\'ensemble',     icon: Icons.Home,        section: 'config' },
    { id: 'config',      label: 'Configuration',       icon: Icons.Settings,    section: 'config' },
    { id: 'personality', label: 'Personnalité & prompt', icon: Icons.Brain,     section: 'config' },
    { id: 'voice',       label: 'Voix & ton',          icon: Icons.Mic,         section: 'config' },

    { id: 'sources',     label: 'Sources URL/PDF',     icon: Icons.Globe,       section: 'know' },
    { id: 'knowledge',   label: 'Connaissances Q&A',   icon: Icons.Document,    section: 'know' },
    { id: 'learnings',   label: 'Apprentissages auto', icon: Icons.Sparkles,    section: 'know' },
    { id: 'dataset',     label: 'Dataset (CSV)',       icon: Icons.Database,    section: 'know',  soon: true },

    { id: 'simulator',   label: 'Simulator (live)',    icon: Icons.Play,        section: 'test' },
    { id: 'abtest',      label: 'A/B testing',         icon: Icons.Beaker,      section: 'test',  soon: true },
    { id: 'conversations', label: 'Conversations',     icon: Icons.Phone2,      section: 'test' },

    { id: 'appointments', label: 'Rendez-vous',        icon: Icons.Calendar,    section: 'biz' },
    { id: 'leads',        label: 'Lead scoring',       icon: Icons.AlertCircle, section: 'biz' },
    { id: 'lines',        label: 'Lignes connectées',  icon: Icons.Headset,     section: 'biz' },

    { id: 'versions',     label: 'Versions',           icon: Icons.Branch,      section: 'hist',  soon: true },
    { id: 'analytics',    label: 'Analytics',          icon: Icons.Chart,       section: 'hist',  soon: true },
];
const BOT_SECTIONS = {
    config: 'CONFIGURATION',
    know:   'CONNAISSANCE',
    test:   'TEST & QUALITÉ',
    biz:    'COMMERCIAL',
    hist:   'HISTORIQUE',
};

const Sidebar = ({ currentBot }) => {
    const { user, route, navigate, logout, sidebarOpen, setSidebarOpen } = useApp();
    const inBotMode = route.view === 'bot-detail' && currentBot;

    const groupedTop = Object.entries(TOP_LEVEL_SECTIONS).map(([key, label]) => ({
        label, items: TOP_LEVEL_NAV.filter(i => i.section === key),
    }));
    const groupedBot = Object.entries(BOT_SECTIONS).map(([key, label]) => ({
        label, items: BOT_NAV.filter(i => i.section === key),
    }));

    return (
        <>
            {sidebarOpen && <div className="sidebar-overlay" onClick={() => setSidebarOpen(false)} />}
            <aside className={`sidebar ${sidebarOpen ? 'open' : ''}`}>
                <div className="sidebar-header">
                    <img src="assets/img/vocal-logotype.svg" alt="Vocal" className="logo" />
                    <span className="studio-pill">Studio</span>
                </div>

                {inBotMode && (
                    <div className="sidebar-context">
                        <button className="sidebar-context-back" onClick={() => navigate('bots')}>
                            <Icons.ChevronLeft className="w-3.5 h-3.5" /> Tous mes bots
                        </button>
                        <div className="sidebar-context-name" title={currentBot.name}>
                            <Icons.Robot className="w-4 h-4" /> {currentBot.name}
                        </div>
                        <div className="sidebar-context-status">
                            <StatusDot status={currentBot.is_active ? 'active' : 'draft'} />
                        </div>
                    </div>
                )}

                <nav className="sidebar-nav">
                    {!inBotMode && groupedTop.map(group => (
                        <div className="nav-section" key={group.label}>
                            <div className="nav-section-title">{group.label}</div>
                            {group.items.map(item => (
                                <button key={item.id}
                                    className={`nav-item ${route.view === item.id ? 'active' : ''}`}
                                    onClick={() => navigate(item.id)}>
                                    <item.icon className="nav-icon" />
                                    {item.label}
                                </button>
                            ))}
                        </div>
                    ))}

                    {inBotMode && groupedBot.map(group => (
                        <div className="nav-section" key={group.label}>
                            <div className="nav-section-title">{group.label}</div>
                            {group.items.map(item => (
                                <button key={item.id}
                                    className={`nav-item ${route.panel === item.id ? 'active' : ''}`}
                                    onClick={() => navigate('bot-detail', { botId: currentBot.id, panel: item.id })}>
                                    <item.icon className="nav-icon" />
                                    {item.label}
                                    {item.soon && <span className="nav-badge-soon">Soon</span>}
                                </button>
                            ))}
                        </div>
                    ))}

                    <div className="nav-section">
                        <div className="nav-section-title">Outils Vocal</div>
                        <a className="nav-item" href={`https://studio.vocal.ch/?token=${encodeURIComponent(localStorage.getItem('vocal_my_token') || '')}#composer`} target="_blank" rel="noopener" title="Voice Studio — Recorder, A/B compare, marketplace voix">
                            <Icons.Mic className="nav-icon" />
                            Voice Studio
                            <span className="nav-badge-soon" style={{ background: 'var(--primary-light)', color: 'var(--primary)' }}>NEW</span>
                        </a>
                        <a className="nav-item" href={CONFIG.MY_URL} target="_blank" rel="noopener" title="my.vocal.ch — Lignes, factures, équipes, intégrations">
                            <Icons.Home className="nav-icon" />
                            my.vocal.ch
                        </a>
                    </div>
                </nav>

                <div className="sidebar-footer">
                    <div className="user-info">
                        <div className="user-avatar">{(user?.name || user?.email || 'U')[0].toUpperCase()}</div>
                        <div className="user-details">
                            <div className="user-name">{user?.name || user?.client?.name || 'Mon compte'}</div>
                            <div className="user-role">{user?.email}</div>
                        </div>
                        <button onClick={logout} title="Déconnexion" className="btn-icon" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)' }}>
                            <Icons.LogOut className="w-4 h-4" />
                        </button>
                    </div>
                </div>
            </aside>
        </>
    );
};

/* ============================================================================
   TOP-LEVEL VIEWS
   ============================================================================ */

// ==================== BOTS LIST ====================
const BotsListView = () => {
    const { navigate, notify } = useApp();
    const [bots, setBots] = useState(null);
    const [creating, setCreating] = useState(false);

    const load = useCallback(async () => {
        const r = await api.get('/my/bots');
        if (r?.error) notify.error(r.error);
        else setBots(r.bots || r.data || []);
    }, [notify]);

    useEffect(() => { load(); }, [load]);

    const createBlank = async () => {
        setCreating(true);
        const r = await api.post('/my/bots', { name: 'Nouveau bot', voice: 'alloy', language: 'fr', greeting: 'Bonjour, je vous écoute.' });
        setCreating(false);
        if (r?.error) return notify.error(r.error);
        notify.success('Bot créé');
        const id = r.bot?.id || r.id;
        if (id) navigate('bot-detail', { botId: id, panel: 'config' });
        else load();
    };

    return (
        <>
            <PageHeader
                title="Mes bots vocaux"
                subtitle="Concevez, entraînez et publiez vos assistants IA en quelques minutes."
                actions={<>
                    <button className="btn btn-secondary btn-sm" onClick={() => navigate('templates')}>
                        <Icons.Layers className="w-4 h-4" /> Partir d'un template
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={createBlank} disabled={creating}>
                        <Icons.Plus className="w-4 h-4" /> Nouveau bot
                    </button>
                </>}
            />
            <div className="page-content">
                {bots === null ? (
                    <div style={{ display: 'flex', justifyContent: 'center', padding: '4rem 0' }}><Spinner size={32} /></div>
                ) : bots.length === 0 ? (
                    <EmptyState
                        icon={Icons.Robot}
                        title="Aucun bot pour l'instant"
                        desc="Créez votre premier bot vocal IA en partant d'une page blanche, ou choisissez un template optimisé pour votre secteur."
                        action={
                            <div className="hstack" style={{ justifyContent: 'center' }}>
                                <button className="btn btn-secondary" onClick={() => navigate('templates')}>
                                    <Icons.Layers className="w-4 h-4" /> Choisir un template
                                </button>
                                <button className="btn btn-primary" onClick={createBlank}>
                                    <Icons.Plus className="w-4 h-4" /> Bot vierge
                                </button>
                            </div>
                        }
                    />
                ) : (
                    <div className="bot-grid">
                        {bots.map(b => (
                            <div key={b.id} className="bot-card" onClick={() => navigate('bot-detail', { botId: b.id })}>
                                <div className="bot-card-header">
                                    <div className="bot-avatar"><Icons.Robot className="w-6 h-6" /></div>
                                    <span className="badge badge-gray text-xs">
                                        <StatusDot status={b.is_active ? 'active' : 'draft'} />
                                    </span>
                                </div>
                                <div>
                                    <h3 className="bot-card-title">{b.name}</h3>
                                    <p className="bot-card-desc">{b.description || 'Aucune description.'}</p>
                                </div>
                                <div className="bot-card-meta">
                                    <span><Icons.Mic className="w-3.5 h-3.5" /> {b.voice || 'alloy'}</span>
                                    <span><Icons.Globe className="w-3.5 h-3.5" /> {(b.language || 'fr').toUpperCase()}</span>
                                    {b.created_at && <span><Icons.Clock className="w-3.5 h-3.5" /> {fmtRelative(b.created_at)}</span>}
                                </div>
                            </div>
                        ))}
                        <div className="bot-create-card" onClick={createBlank}>
                            <div className="bot-create-icon"><Icons.Plus className="w-7 h-7" /></div>
                            <div className="bot-create-text">Nouveau bot</div>
                            <div className="text-xs text-muted">Page blanche · 1 min de setup</div>
                        </div>
                    </div>
                )}
            </div>
        </>
    );
};

// ==================== TEMPLATES ====================
const TemplatesView = () => {
    const { navigate, notify } = useApp();
    const [picked, setPicked] = useState(null);
    const [creating, setCreating] = useState(false);

    const instantiate = async (tpl) => {
        setCreating(true);
        const r = await api.post('/my/bots', { ...tpl.defaults, source_template: tpl.id });
        setCreating(false);
        if (r?.error) return notify.error(r.error);
        notify.success(`Bot "${tpl.title}" créé`);
        const id = r.bot?.id || r.id;
        if (id) navigate('bot-detail', { botId: id, panel: 'personality' });
    };

    return (
        <>
            <PageHeader
                title="Templates par secteur"
                subtitle="Démarrez avec une configuration optimisée pour votre métier — modifiable ensuite."
                actions={<button className="btn btn-secondary btn-sm" onClick={() => navigate('bots')}><Icons.ChevronLeft className="w-4 h-4" /> Retour</button>}
            />
            <div className="page-content">
                <div className="template-grid">
                    {SECTOR_TEMPLATES.map(tpl => {
                        const TplIcon = Icons[tpl.iconKey] || Icons.Sparkles;
                        return (
                        <div key={tpl.id} className="template-card" onClick={() => setPicked(tpl)}>
                            <div className={`template-icon ${tpl.kind}`}><TplIcon className="w-7 h-7" /></div>
                            <h3 className="template-title">{tpl.title}</h3>
                            <p className="template-desc">{tpl.desc}</p>
                            <div className="template-features">
                                {tpl.features.map(f => <span key={f} className="badge badge-gray">{f}</span>)}
                            </div>
                        </div>
                        );
                    })}
                </div>
            </div>
            {picked && (
                <Modal onClose={() => setPicked(null)} title={`Template : ${picked.title}`}>
                    <p className="text-muted text-sm">{picked.desc}</p>
                    <div className="card-flat" style={{ marginTop: '1rem' }}>
                        <div className="text-xs text-bold" style={{ marginBottom: '0.5rem' }}>Aperçu du prompt</div>
                        <pre style={{ background: '#0f172a', color: '#e2e8f0', padding: '1rem', borderRadius: 12, fontSize: '0.78rem', overflowX: 'auto', whiteSpace: 'pre-wrap', margin: 0 }}>{picked.defaults.instructions}</pre>
                    </div>
                    <div className="card-flat" style={{ marginTop: '1rem' }}>
                        <div className="text-xs text-bold" style={{ marginBottom: '0.5rem' }}>Message d'accueil</div>
                        <div className="text-sm">"{picked.defaults.greeting}"</div>
                    </div>
                    <div className="hstack" style={{ marginTop: '1rem', justifyContent: 'flex-end' }}>
                        <button className="btn btn-secondary" onClick={() => setPicked(null)}>Annuler</button>
                        <button className="btn btn-primary" onClick={() => instantiate(picked)} disabled={creating}>
                            {creating ? 'Création…' : 'Créer ce bot'}
                        </button>
                    </div>
                </Modal>
            )}
        </>
    );
};

// ==================== MARKETPLACE ====================
const MarketplaceView = () => {
    const { navigate, notify } = useApp();
    const [voices, setVoices] = useState(null);
    const [filter, setFilter] = useState('');

    useEffect(() => {
        let cancelled = false;
        api.get('/marketplace/voices').then(r => {
            if (cancelled) return;
            if (Array.isArray(r?.voices)) setVoices(r.voices);
            else setVoices(MARKETPLACE_VOICES_DEMO);
        });
        return () => { cancelled = true; };
    }, []);

    const filtered = useMemo(() => {
        const list = voices || [];
        if (!filter) return list;
        const q = filter.toLowerCase();
        return list.filter(v => v.name.toLowerCase().includes(q) || v.tags.some(t => t.toLowerCase().includes(q)) || (v.creator || '').toLowerCase().includes(q));
    }, [voices, filter]);

    return (
        <>
            <PageHeader
                title="Marketplace de voix custom"
                subtitle="Voix premium clonées par des créateurs suisses, facturées à l'usage."
                actions={<button className="btn btn-secondary btn-sm" onClick={() => navigate('bots')}><Icons.ChevronLeft className="w-4 h-4" /> Retour</button>}
            />
            <div className="page-content">
                <SoonBanner endpoint="GET /marketplace/voices">
                    <b>Marketplace en aperçu.</b> Affiche 6 voix de démo. Pour passer en live, créez les tables <code>voice_marketplace_listings</code>, <code>voice_marketplace_subscriptions</code>, <code>voice_marketplace_payouts</code> (voir <code>YANNICK/V2_FEATURES.md</code>) puis branchez les endpoints.
                </SoonBanner>
                <div className="card-flat" style={{ marginBottom: '1rem' }}>
                    <div className="hstack" style={{ alignItems: 'center' }}>
                        <Icons.Search className="w-4 h-4 text-muted" />
                        <input className="form-input" style={{ flex: 1, border: 'none', boxShadow: 'none', background: 'transparent', padding: '0.4rem 0' }} placeholder="Filtrer par nom, langue, créateur…" value={filter} onChange={e => setFilter(e.target.value)} />
                    </div>
                </div>

                {voices === null ? (
                    <div style={{ display: 'flex', justifyContent: 'center', padding: '3rem' }}><Spinner /></div>
                ) : (
                    <div className="card-grid cols-3">
                        {filtered.map(v => (
                            <div key={v.id} className="voice-card">
                                <div className="voice-card-top">
                                    <div className="voice-avatar">{v.name[0]}</div>
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div className="name">{v.name}</div>
                                        <div className="creator">par {v.creator}</div>
                                    </div>
                                    <button className="btn-icon btn btn-ghost" title="Écouter une démo" onClick={() => notify.info('Démo audio bientôt disponible')}>
                                        <Icons.Play className="w-4 h-4" />
                                    </button>
                                </div>
                                <div className="text-sm text-muted">{v.desc}</div>
                                <div className="voice-tags">
                                    {v.tags.map(t => <span key={t} className="badge badge-purple">{t}</span>)}
                                </div>
                                <div className="voice-waveform">
                                    {Array.from({ length: 28 }).map((_, i) => (
                                        <span key={i} className="bar" style={{ height: `${20 + Math.abs(Math.sin(i * 1.7 + v.id.length)) * 70}%` }} />
                                    ))}
                                </div>
                                <div className="voice-card-footer">
                                    <div>
                                        <div className="voice-price">CHF {v.price_chf}<small> / mois</small></div>
                                        <div className="text-xs text-muted">{v.calls_used.toLocaleString('fr-CH')} appels ce mois</div>
                                    </div>
                                    <button className="btn btn-primary btn-sm" onClick={() => notify.info('Souscription bientôt disponible')}>
                                        S'abonner
                                    </button>
                                </div>
                            </div>
                        ))}
                    </div>
                )}

                <div className="card" style={{ marginTop: '2rem' }}>
                    <div className="card-header">
                        <div>
                            <h3 className="card-title">Vendre votre voix sur Vocal</h3>
                            <p className="card-subtitle">Publiez vos voix clonées et touchez 70% du revenu. KYC Stripe Identity requis.</p>
                        </div>
                        <button className="btn btn-secondary btn-sm" onClick={() => notify.info('Onboarding créateur bientôt disponible')}>
                            Devenir créateur
                        </button>
                    </div>
                </div>
            </div>
        </>
    );
};

// ==================== GLOBAL ANALYTICS ====================
const GlobalAnalyticsView = () => {
    const { navigate } = useApp();
    return (
        <>
            <PageHeader title="Analytics globales" subtitle="KPIs cross-bots de votre compte." />
            <div className="page-content">
                <SoonBanner endpoint="GET /my/bots/analytics?range=30d">
                    Rapports cross-bots agrégés (volume d'appels par bot, taux de résolution, escalade humaine, NPS, coût total OpenAI).
                </SoonBanner>
                <div className="kpi-grid">
                    <KpiCard label="Bots actifs" value="—" />
                    <KpiCard label="Appels traités (30j)" value="—" />
                    <KpiCard label="Taux de résolution" value="—" />
                    <KpiCard label="Coût IA estimé" value="— CHF" />
                </div>
                <div className="card">
                    <div className="card-header"><h3 className="card-title">Performance par bot</h3></div>
                    <div className="card-body text-center text-muted text-sm" style={{ padding: '3rem 1rem' }}>
                        Visualisations à connecter aux endpoints d'analytics.
                    </div>
                </div>
            </div>
        </>
    );
};

const KpiCard = ({ label, value, trend }) => (
    <div className="kpi">
        <div className="kpi-label">{label}</div>
        <div className="kpi-value">{value}</div>
        {trend && <div className={`kpi-trend ${trend.dir}`}>{trend.text}</div>}
    </div>
);

// ==================== ACCOUNT SETTINGS ====================
const AccountSettingsView = () => {
    const { user, logout } = useApp();
    return (
        <>
            <PageHeader title="Réglages compte" subtitle="Profil, organisation et préférences Bot Studio." />
            <div className="page-content">
                <div className="card" style={{ maxWidth: 720 }}>
                    <div className="card-header"><h3 className="card-title">Profil</h3></div>
                    <div className="card-body vstack gap-lg">
                        <div className="form-row">
                            <label>Email</label>
                            <input type="text" disabled value={user?.email || ''} />
                        </div>
                        <div className="form-row">
                            <label>Nom</label>
                            <input type="text" disabled value={user?.name || ''} />
                        </div>
                        <div className="form-help">
                            Pour modifier ces informations, rendez-vous sur <a href={CONFIG.MY_URL} target="_blank" rel="noopener">my.vocal.ch</a> &rsaquo; Paramètres.
                        </div>
                    </div>
                </div>
                <div className="card" style={{ marginTop: '1rem', maxWidth: 720 }}>
                    <div className="card-header"><h3 className="card-title">Sessions</h3></div>
                    <div className="card-body">
                        <button className="btn btn-danger btn-sm" onClick={logout}><Icons.LogOut className="w-4 h-4" /> Se déconnecter</button>
                    </div>
                </div>
            </div>
        </>
    );
};

// ==================== MODAL ====================
const Modal = ({ title, children, onClose }) => (
    <div style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(15,23,42,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1rem', animation: 'fadeIn 0.15s ease' }} onClick={onClose}>
        <div className="card" style={{ maxWidth: 560, width: '100%', maxHeight: '90vh', overflow: 'auto' }} onClick={e => e.stopPropagation()}>
            <div className="card-header">
                <h3 className="card-title">{title}</h3>
                <button className="btn-icon btn btn-ghost" onClick={onClose}><Icons.X className="w-4 h-4" /></button>
            </div>
            <div className="card-body">{children}</div>
        </div>
    </div>
);

/* ============================================================================
   BOT DETAIL — router for the 16 panels
   ============================================================================ */

const BotDetailView = ({ botId, panel, onBotLoaded }) => {
    const { navigate, notify } = useApp();
    const [bot, setBot] = useState(null);
    const [loading, setLoading] = useState(true);
    const [form, setForm] = useState({});
    const [editMode, setEditMode] = useState(false);
    const [saving, setSaving] = useState(false);

    const load = useCallback(async () => {
        setLoading(true);
        const r = await api.get(`/my/bots/${botId}`);
        if (r?.error) {
            notify.error(r.error);
            setLoading(false);
            if (r._status === 404) navigate('bots');
            return;
        }
        const b = r.bot || r;
        setBot(b);
        onBotLoaded?.(b);
        setForm({
            name: b.name || '', description: b.description || '', instructions: b.instructions || '',
            voice: b.voice || 'alloy', language: b.language || 'fr', greeting: b.greeting || '',
            human_transfer_number: b.human_transfer_number || '',
            learn_from_calls: !!b.learn_from_calls,
            max_call_duration: b.max_call_duration || 600, is_active: !!b.is_active,
            appointments_enabled: !!b.appointments_enabled,
            appointment_duration_minutes: b.appointment_duration_minutes || 30,
            appointment_buffer_minutes: b.appointment_buffer_minutes || 0,
            appointment_business_hours: b.appointment_business_hours || '',
            appointment_notify_email: b.appointment_notify_email || '',
            appointment_max_days_ahead: b.appointment_max_days_ahead || 30,
            lead_scoring_enabled: !!b.lead_scoring_enabled,
            lead_scoring_prompt: b.lead_scoring_prompt || '',
            custom_voice_provider: b.custom_voice_provider || '',
            custom_voice_id: b.custom_voice_id || '',
        });
        setLoading(false);
    }, [botId, notify, navigate, onBotLoaded]);

    useEffect(() => { load(); }, [load]);

    const save = async (overrides = {}) => {
        setSaving(true);
        const r = await api.put(`/my/bots/${botId}`, { ...form, ...overrides });
        setSaving(false);
        if (r?.error) return notify.error(r.error);
        notify.success('Bot mis à jour');
        setEditMode(false);
        load();
    };

    const remove = async () => {
        if (!confirm('Supprimer ce bot ? Cette action est irréversible.')) return;
        const r = await api.del(`/my/bots/${botId}`);
        if (r?.error) return notify.error(r.error);
        notify.success('Bot supprimé');
        navigate('bots');
    };

    const togglePublish = async () => {
        const next = !bot.is_active;
        await save({ is_active: next });
    };

    if (loading || !bot) {
        return (
            <>
                <PageHeader title="Chargement…" />
                <div className="page-content" style={{ display: 'flex', justifyContent: 'center', padding: '4rem' }}><Spinner size={32} /></div>
            </>
        );
    }

    const breadcrumb = (
        <>
            <a href="#bots" onClick={(e) => { e.preventDefault(); navigate('bots'); }}>Mes bots</a>
            <span className="sep">›</span>
            <span>{bot.name}</span>
        </>
    );

    const actions = (
        <>
            <span className="badge badge-gray"><StatusDot status={bot.is_active ? 'active' : 'draft'} /></span>
            {bot.is_active
                ? <button className="btn btn-secondary btn-sm" onClick={togglePublish} disabled={saving}><Icons.X className="w-4 h-4" /> Mettre en pause</button>
                : <button className="btn btn-success btn-sm" onClick={togglePublish} disabled={saving}><Icons.Check className="w-4 h-4" /> Publier</button>
            }
            <button className="btn btn-danger btn-sm" onClick={remove}><Icons.Trash className="w-4 h-4" /></button>
        </>
    );

    const PANEL_TITLES = {
        overview: 'Vue d\'ensemble', config: 'Configuration générale', personality: 'Personnalité & prompt',
        voice: 'Voix & ton', sources: 'Sources URL/PDF', knowledge: 'Connaissances Q&A',
        learnings: 'Apprentissages auto', dataset: 'Dataset', simulator: 'Simulator (chat live)',
        abtest: 'A/B testing', conversations: 'Conversations', appointments: 'Rendez-vous',
        leads: 'Lead scoring', lines: 'Lignes connectées', versions: 'Versions', analytics: 'Analytics',
    };

    return (
        <>
            <PageHeader
                title={PANEL_TITLES[panel] || bot.name}
                subtitle={bot.description}
                breadcrumb={breadcrumb}
                actions={actions}
            />
            <div className="page-content">
                {panel === 'overview'      && <BotOverviewPanel bot={bot} navigate={navigate} />}
                {panel === 'config'        && <BotConfigPanel form={form} setForm={setForm} editMode={editMode} setEditMode={setEditMode} saving={saving} onSave={save} onCancel={() => { setEditMode(false); load(); }} />}
                {panel === 'personality'   && <BotPersonalityPanel bot={bot} form={form} setForm={setForm} saving={saving} onSave={save} />}
                {panel === 'voice'         && <BotVoicePanel form={form} setForm={setForm} saving={saving} onSave={save} />}
                {panel === 'sources'       && <BotSourcesPanel botId={botId} />}
                {panel === 'knowledge'     && <BotKnowledgePanel botId={botId} />}
                {panel === 'learnings'     && <BotLearningsPanel botId={botId} />}
                {panel === 'dataset'       && <BotDatasetPanel botId={botId} />}
                {panel === 'simulator'     && <BotSimulatorPanel bot={bot} />}
                {panel === 'abtest'        && <BotABTestPanel botId={botId} />}
                {panel === 'conversations' && <BotConversationsPanel botId={botId} />}
                {panel === 'appointments'  && <BotAppointmentsPanel form={form} setForm={setForm} saving={saving} onSave={save} />}
                {panel === 'leads'         && <BotLeadsPanel form={form} setForm={setForm} saving={saving} onSave={save} />}
                {panel === 'lines'         && <BotLinesPanel botId={botId} />}
                {panel === 'versions'      && <BotVersionsPanel botId={botId} />}
                {panel === 'analytics'     && <BotAnalyticsPanel botId={botId} />}
            </div>
        </>
    );
};

/* ============================================================================
   BOT PANELS
   ============================================================================ */

// ---------- Overview ----------
const BotOverviewPanel = ({ bot, navigate }) => (
    <>
        <div className="kpi-grid">
            <KpiCard label="Statut" value={bot.is_active ? 'En production' : 'Brouillon'} />
            <KpiCard label="Voix" value={bot.voice || 'alloy'} />
            <KpiCard label="Langue" value={(bot.language || 'fr').toUpperCase()} />
            <KpiCard label="Apprentissage auto" value={bot.learn_from_calls ? 'Activé' : 'Désactivé'} />
        </div>

        <div className="card-grid cols-2">
            <div className="card">
                <div className="card-header">
                    <h3 className="card-title">Démarrage rapide</h3>
                </div>
                <div className="card-body vstack gap-lg">
                    <ChecklistItem done={!!bot.instructions} title="Personnalité & prompt" panel="personality" navigate={navigate} botId={bot.id} />
                    <ChecklistItem done={!!bot.voice} title="Voix sélectionnée" panel="voice" navigate={navigate} botId={bot.id} />
                    <ChecklistItem done={!!bot.greeting} title="Message d'accueil" panel="config" navigate={navigate} botId={bot.id} />
                    <ChecklistItem done={false} title="Sources URL/PDF importées" panel="sources" navigate={navigate} botId={bot.id} />
                    <ChecklistItem done={false} title="Testé dans le Simulator" panel="simulator" navigate={navigate} botId={bot.id} />
                    <ChecklistItem done={!!bot.is_active} title="Publié sur au moins une ligne" panel="lines" navigate={navigate} botId={bot.id} />
                </div>
            </div>
            <div className="card">
                <div className="card-header">
                    <h3 className="card-title">Paramètres rapides</h3>
                </div>
                <div className="card-body vstack gap-lg">
                    <div>
                        <div className="text-xs text-muted text-bold" style={{ marginBottom: '0.3rem' }}>Numéro de transfert humain</div>
                        <div className="text-sm">{bot.human_transfer_number || <span className="text-soft">Non configuré</span>}</div>
                    </div>
                    <div>
                        <div className="text-xs text-muted text-bold" style={{ marginBottom: '0.3rem' }}>Durée max d'appel</div>
                        <div className="text-sm">{fmtDuration(bot.max_call_duration || 600)}</div>
                    </div>
                    <div>
                        <div className="text-xs text-muted text-bold" style={{ marginBottom: '0.3rem' }}>Rendez-vous</div>
                        <div className="text-sm">{bot.appointments_enabled ? `Activé · ${bot.appointment_duration_minutes || 30} min` : <span className="text-soft">Désactivé</span>}</div>
                    </div>
                    <div>
                        <div className="text-xs text-muted text-bold" style={{ marginBottom: '0.3rem' }}>Lead scoring</div>
                        <div className="text-sm">{bot.lead_scoring_enabled ? 'Activé' : <span className="text-soft">Désactivé</span>}</div>
                    </div>
                </div>
            </div>
        </div>
    </>
);

const ChecklistItem = ({ done, title, panel, navigate, botId }) => (
    <button className="hstack" style={{ width: '100%', background: 'none', border: 'none', padding: '0.5rem 0', cursor: 'pointer', textAlign: 'left' }} onClick={() => navigate('bot-detail', { botId, panel })}>
        <span style={{ width: 22, height: 22, borderRadius: '50%', background: done ? '#dcfce7' : 'var(--bg-soft)', color: done ? '#166534' : 'var(--text-soft)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.7rem', fontWeight: 700, flexShrink: 0 }}>
            {done ? <Icons.Check className="w-3 h-3" /> : '○'}
        </span>
        <span style={{ flex: 1, fontSize: '0.875rem', fontWeight: 600, color: done ? 'var(--text-main)' : 'var(--text-muted)' }}>{title}</span>
        <Icons.ChevronRight className="w-4 h-4 text-muted" />
    </button>
);

// ---------- Config (général) ----------
const BotConfigPanel = ({ form, setForm, editMode, setEditMode, saving, onSave, onCancel }) => (
    <div className="card">
        <div className="card-header">
            <div>
                <h3 className="card-title">Configuration générale</h3>
                <p className="card-subtitle">Nom, description, comportement de base, transfert humain.</p>
            </div>
            {!editMode
                ? <button className="btn btn-primary btn-sm" onClick={() => setEditMode(true)}><Icons.Edit className="w-4 h-4" /> Modifier</button>
                : <div className="hstack">
                    <button className="btn btn-secondary btn-sm" onClick={onCancel}>Annuler</button>
                    <button className="btn btn-primary btn-sm" onClick={() => onSave()} disabled={saving}>{saving ? 'Enregistrement…' : 'Enregistrer'}</button>
                </div>
            }
        </div>
        <div className="card-body">
            <div className="form-row">
                <label>Nom du bot</label>
                <input type="text" disabled={!editMode} value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))} />
            </div>
            <div className="form-row">
                <label>Description courte</label>
                <input type="text" disabled={!editMode} value={form.description} onChange={e => setForm(f => ({ ...f, description: e.target.value }))} placeholder="Ex: Assistant vocal pour mon restaurant à Lausanne" />
            </div>
            <div className="form-row">
                <label>Message d'accueil</label>
                <textarea rows={2} disabled={!editMode} value={form.greeting} onChange={e => setForm(f => ({ ...f, greeting: e.target.value }))} placeholder="Bonjour, restaurant Le Lac, j'écoute !" />
                <span className="form-help">Le tout premier message prononcé par le bot quand l'appel décroche.</span>
            </div>
            <div className="form-row row-grid">
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Numéro de transfert humain</label>
                    <input type="text" disabled={!editMode} value={form.human_transfer_number} onChange={e => setForm(f => ({ ...f, human_transfer_number: e.target.value }))} placeholder="+41 79 ..." />
                </div>
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Durée max d'appel (s)</label>
                    <input type="number" min="60" max="3600" disabled={!editMode} value={form.max_call_duration} onChange={e => setForm(f => ({ ...f, max_call_duration: parseInt(e.target.value || '600') }))} />
                </div>
            </div>
            <label className="toggle-row">
                <input type="checkbox" disabled={!editMode} checked={form.learn_from_calls} onChange={e => setForm(f => ({ ...f, learn_from_calls: e.target.checked }))} />
                <span>Apprendre automatiquement des conversations terminées (extraction Q&R)</span>
            </label>
            <label className="toggle-row" style={{ marginTop: '0.5rem' }}>
                <input type="checkbox" disabled={!editMode} checked={form.is_active} onChange={e => setForm(f => ({ ...f, is_active: e.target.checked }))} />
                <span>Bot actif (reçoit des appels en production)</span>
            </label>
        </div>
    </div>
);

// ---------- Personality (prompt) ----------
const BotPersonalityPanel = ({ bot, form, setForm, saving, onSave }) => {
    const [language, setLanguage] = useState(form.language);

    return (
        <>
            <div className="card-grid cols-2" style={{ marginBottom: '1rem' }}>
                <div className="card">
                    <div className="card-header"><h3 className="card-title">Langue de réponse</h3></div>
                    <div className="card-body">
                        <div className="form-row">
                            <select value={language} onChange={e => { setLanguage(e.target.value); setForm(f => ({ ...f, language: e.target.value })); }}>
                                {LANGS.map(l => <option key={l.id} value={l.id}>{l.label}</option>)}
                            </select>
                        </div>
                    </div>
                </div>
                <div className="card">
                    <div className="card-header"><h3 className="card-title">Conseils prompt</h3></div>
                    <div className="card-body text-sm text-muted vstack gap-sm">
                        <div>1. Définir le rôle (ex: "Tu es l'assistante du Dr X")</div>
                        <div>2. Limiter le périmètre ("Tu NE donnes JAMAIS de conseil médical")</div>
                        <div>3. Décrire le ton (chaleureux, concis, professionnel)</div>
                        <div>4. Définir l'escalade ("Transfère au {`{TRANSFERT}`} si...")</div>
                    </div>
                </div>
            </div>

            <div className="card">
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Personnalité & instructions</h3>
                        <p className="card-subtitle">Le prompt système qui définit qui est le bot et comment il se comporte.</p>
                    </div>
                    <button className="btn btn-primary btn-sm" onClick={() => onSave({ instructions: form.instructions, language: form.language })} disabled={saving}>
                        <Icons.Check className="w-4 h-4" /> {saving ? 'Enregistrement…' : 'Sauvegarder'}
                    </button>
                </div>
                <div className="card-body">
                    <textarea
                        className="form-input code-textarea"
                        rows={16}
                        value={form.instructions || ''}
                        onChange={e => setForm(f => ({ ...f, instructions: e.target.value }))}
                        placeholder="Tu es l'assistant vocal de [entreprise]. Tu es chaleureux, concis, professionnel. Tu peux..."
                    />
                    <div className="text-xs text-muted" style={{ marginTop: '0.5rem' }}>
                        {(form.instructions || '').length} caractères · ~{Math.ceil((form.instructions || '').length / 4)} tokens
                    </div>
                </div>
            </div>

            <div className="card" style={{ marginTop: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Versioning</h3>
                        <p className="card-subtitle">Historique des modifications du prompt — rollback en 1 clic.</p>
                    </div>
                    <span className="badge badge-purple">Soon</span>
                </div>
                <div className="card-body">
                    <SoonBanner endpoint="GET /my/bots/:id/versions">
                        Le versioning enregistre chaque modification du prompt avec auteur, timestamp et diff. Rollback en 1 clic vers une version antérieure stable.
                    </SoonBanner>
                </div>
            </div>
        </>
    );
};

// ---------- Voice ----------
const BotVoicePanel = ({ form, setForm, saving, onSave }) => {
    const { notify } = useApp();
    const [clones, setClones] = useState([]);

    useEffect(() => {
        api.get('/my/voice-clones').then(r => {
            if (Array.isArray(r?.clones)) setClones(r.clones);
            else if (Array.isArray(r?.data)) setClones(r.data);
        });
    }, []);

    const pick = (provider, id) => {
        setForm(f => ({ ...f, custom_voice_provider: provider, custom_voice_id: id, voice: provider === 'openai' ? id : f.voice }));
    };

    return (
        <>
            <div className="card">
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Voix OpenAI standards</h3>
                        <p className="card-subtitle">Incluses, qualité broadcast, faible latence.</p>
                    </div>
                </div>
                <div className="card-body">
                    <div className="card-grid cols-3">
                        {VOICES.map(v => {
                            const selected = (form.custom_voice_provider || 'openai') === 'openai' && (form.voice === v.id);
                            return (
                                <button key={v.id} className="card-flat" style={{ borderColor: selected ? 'var(--primary)' : 'var(--border)', boxShadow: selected ? '0 0 0 3px rgba(99,102,241,0.15)' : 'var(--shadow-sm)', cursor: 'pointer', textAlign: 'left', background: 'var(--bg-white)' }} onClick={() => pick('openai', v.id)}>
                                    <div className="hstack">
                                        <div className="bot-avatar" style={{ width: 32, height: 32, borderRadius: 8 }}><Icons.Mic className="w-4 h-4" /></div>
                                        <div style={{ flex: 1 }}>
                                            <div className="text-bold text-sm">{v.label}</div>
                                            <div className="text-xs text-muted">openai · {v.id}</div>
                                        </div>
                                        {selected && <span className="badge badge-success">Active</span>}
                                    </div>
                                </button>
                            );
                        })}
                    </div>
                </div>
            </div>

            <div className="card" style={{ marginTop: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Vos voix custom (clones)</h3>
                        <p className="card-subtitle">Voix clonées via Studio voix · ElevenLabs / Vocal Studio.</p>
                    </div>
                    <a href={`${CONFIG.MY_URL}#studio`} target="_blank" rel="noopener" className="btn btn-secondary btn-sm">
                        <Icons.External className="w-4 h-4" /> Ouvrir Studio voix
                    </a>
                </div>
                <div className="card-body">
                    {clones.length === 0
                        ? <div className="text-sm text-muted">Aucune voix clonée pour l'instant. <a href={`${CONFIG.MY_URL}#voice-clones`} target="_blank" rel="noopener">Cloner une voix</a></div>
                        : <div className="card-grid cols-3">{clones.map(c => {
                            const selected = form.custom_voice_provider === c.provider && form.custom_voice_id === c.provider_voice_id;
                            return (
                                <button key={c.id} className="card-flat" style={{ borderColor: selected ? 'var(--primary)' : 'var(--border)', boxShadow: selected ? '0 0 0 3px rgba(99,102,241,0.15)' : 'var(--shadow-sm)', cursor: 'pointer', textAlign: 'left' }} onClick={() => pick(c.provider, c.provider_voice_id)}>
                                    <div className="hstack">
                                        <div className="bot-avatar" style={{ width: 32, height: 32, borderRadius: 8 }}><Icons.Sparkles className="w-4 h-4" /></div>
                                        <div style={{ flex: 1 }}>
                                            <div className="text-bold text-sm">{c.name || c.provider_voice_id}</div>
                                            <div className="text-xs text-muted">{c.provider}</div>
                                        </div>
                                        {selected && <span className="badge badge-success">Active</span>}
                                    </div>
                                </button>
                            );
                        })}</div>
                    }
                </div>
            </div>

            <div className="card" style={{ marginTop: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Marketplace</h3>
                        <p className="card-subtitle">Voix premium clonées par des créateurs suisses, facturées à l'usage.</p>
                    </div>
                    <a href="#marketplace" className="btn btn-secondary btn-sm">Parcourir le catalogue <Icons.ChevronRight className="w-4 h-4" /></a>
                </div>
            </div>

            <div style={{ position: 'sticky', bottom: '1rem', marginTop: '1.5rem', display: 'flex', justifyContent: 'flex-end' }}>
                <button className="btn btn-primary" onClick={() => onSave({ voice: form.voice, custom_voice_provider: form.custom_voice_provider, custom_voice_id: form.custom_voice_id })} disabled={saving}>
                    {saving ? 'Enregistrement…' : 'Enregistrer la voix'}
                </button>
            </div>
        </>
    );
};

// ---------- Sources ----------
const BotSourcesPanel = ({ botId }) => {
    const { notify } = useApp();
    const [sources, setSources] = useState([]);
    const [loading, setLoading] = useState(true);
    const [adding, setAdding] = useState(false);
    const [url, setUrl] = useState('');

    const load = useCallback(async () => {
        setLoading(true);
        const r = await api.get(`/my/bots/${botId}/sources`);
        if (r?.error && r._status !== 404) notify.error(r.error);
        setSources(r?.sources || r?.data || []);
        setLoading(false);
    }, [botId, notify]);
    useEffect(() => { load(); }, [load]);

    const add = async (e) => {
        e.preventDefault();
        if (!url) return;
        setAdding(true);
        const r = await api.post(`/my/bots/${botId}/sources`, { url, type: 'url' });
        setAdding(false);
        if (r?.error) return notify.error(r.error);
        notify.success('Source ajoutée — scraping dans les 10 min');
        setUrl('');
        load();
    };
    const remove = async (sid) => {
        if (!confirm('Supprimer cette source ?')) return;
        await api.del(`/my/bots/${botId}/sources/${sid}`);
        load();
    };

    return (
        <>
            <div className="card" style={{ marginBottom: '1rem' }}>
                <div className="card-header">
                    <div><h3 className="card-title">Ajouter une source</h3>
                        <p className="card-subtitle">URL de votre site, page de FAQ, PDF en ligne. Scraping toutes les 10 min.</p>
                    </div>
                </div>
                <div className="card-body">
                    <form onSubmit={add} className="hstack" style={{ gap: '0.5rem' }}>
                        <input className="form-input" placeholder="https://votre-site.ch/faq" value={url} onChange={e => setUrl(e.target.value)} style={{ flex: 1 }} />
                        <button type="submit" className="btn btn-primary" disabled={adding || !url}>
                            <Icons.Plus className="w-4 h-4" /> Ajouter
                        </button>
                    </form>
                </div>
            </div>

            <div className="table-container">
                <table className="data-table">
                    <thead><tr><th>Source</th><th>Type</th><th>Dernier scraping</th><th>Statut</th><th></th></tr></thead>
                    <tbody>
                        {loading ? <tr><td colSpan={5} style={{ textAlign: 'center', padding: '2rem' }}><Spinner /></td></tr>
                        : sources.length === 0 ? <tr><td colSpan={5} style={{ textAlign: 'center', padding: '2rem' }} className="text-muted">Aucune source. Ajoutez votre site pour que le bot apprenne.</td></tr>
                        : sources.map(s => (
                            <tr key={s.id}>
                                <td className="text-mono text-xs" style={{ maxWidth: 280, overflow: 'hidden', textOverflow: 'ellipsis' }}>{s.url}</td>
                                <td><span className="badge badge-info">{s.type || 'url'}</span></td>
                                <td className="text-sm text-muted">{fmtRelative(s.last_scraped_at)}</td>
                                <td><span className={`badge ${s.status === 'ok' ? 'badge-success' : s.status === 'error' ? 'badge-danger' : 'badge-gray'}`}>{s.status || 'pending'}</span></td>
                                <td><button className="btn-icon btn btn-ghost" onClick={() => remove(s.id)}><Icons.Trash className="w-4 h-4" /></button></td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </>
    );
};

// ---------- Knowledge ----------
const BotKnowledgePanel = ({ botId }) => {
    const { notify } = useApp();
    const [items, setItems] = useState([]);
    const [loading, setLoading] = useState(true);
    const [editing, setEditing] = useState(null);

    const load = useCallback(async () => {
        setLoading(true);
        const r = await api.get(`/my/bots/${botId}/knowledge`);
        if (r?.error && r._status !== 404) notify.error(r.error);
        setItems(r?.knowledge || r?.data || []);
        setLoading(false);
    }, [botId, notify]);
    useEffect(() => { load(); }, [load]);

    const save = async (it) => {
        const isNew = !it.id;
        const r = isNew
            ? await api.post(`/my/bots/${botId}/knowledge`, it)
            : await api.put(`/my/bots/${botId}/knowledge/${it.id}`, it);
        if (r?.error) return notify.error(r.error);
        notify.success('Q&A enregistrée');
        setEditing(null);
        load();
    };
    const remove = async (id) => {
        if (!confirm('Supprimer ?')) return;
        await api.del(`/my/bots/${botId}/knowledge/${id}`);
        load();
    };

    return (
        <>
            <div className="card" style={{ marginBottom: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Connaissances Q&A</h3>
                        <p className="card-subtitle">Questions/réponses manuelles utilisées en priorité par le bot.</p>
                    </div>
                    <button className="btn btn-primary btn-sm" onClick={() => setEditing({ question: '', answer: '', category: '' })}>
                        <Icons.Plus className="w-4 h-4" /> Nouvelle Q&A
                    </button>
                </div>
            </div>

            {loading ? <div style={{ textAlign: 'center', padding: '2rem' }}><Spinner /></div>
            : items.length === 0 ? <EmptyState icon={Icons.Document} title="Aucune Q&A" desc="Ajoutez vos questions/réponses fréquentes pour que le bot réponde sans hallucination." />
            : <div className="vstack gap-sm">{items.map(it => (
                <div key={it.id} className="card-flat hstack" style={{ alignItems: 'flex-start' }}>
                    <div style={{ flex: 1 }}>
                        <div className="text-bold text-sm" style={{ marginBottom: '0.3rem' }}>{it.question}</div>
                        <div className="text-sm text-muted">{it.answer}</div>
                        {it.category && <span className="badge badge-gray text-xs" style={{ marginTop: '0.4rem' }}>{it.category}</span>}
                    </div>
                    <div className="hstack gap-sm">
                        <button className="btn-icon btn btn-ghost" onClick={() => setEditing(it)}><Icons.Edit className="w-4 h-4" /></button>
                        <button className="btn-icon btn btn-ghost" onClick={() => remove(it.id)}><Icons.Trash className="w-4 h-4" /></button>
                    </div>
                </div>
            ))}</div>}

            {editing && (
                <Modal title={editing.id ? 'Éditer la Q&A' : 'Nouvelle Q&A'} onClose={() => setEditing(null)}>
                    <div className="form-row">
                        <label>Question</label>
                        <input value={editing.question} onChange={e => setEditing({ ...editing, question: e.target.value })} placeholder="Quels sont vos horaires ?" />
                    </div>
                    <div className="form-row">
                        <label>Réponse</label>
                        <textarea rows={4} value={editing.answer} onChange={e => setEditing({ ...editing, answer: e.target.value })} placeholder="Nous sommes ouverts du lundi au vendredi de 8h à 18h." />
                    </div>
                    <div className="form-row">
                        <label>Catégorie (optionnel)</label>
                        <input value={editing.category || ''} onChange={e => setEditing({ ...editing, category: e.target.value })} placeholder="horaires, tarifs, contact…" />
                    </div>
                    <div className="hstack" style={{ justifyContent: 'flex-end' }}>
                        <button className="btn btn-secondary" onClick={() => setEditing(null)}>Annuler</button>
                        <button className="btn btn-primary" onClick={() => save(editing)} disabled={!editing.question || !editing.answer}>Enregistrer</button>
                    </div>
                </Modal>
            )}
        </>
    );
};

// ---------- Learnings ----------
const BotLearningsPanel = ({ botId }) => {
    const { notify } = useApp();
    const [items, setItems] = useState([]);
    const [loading, setLoading] = useState(true);

    const load = useCallback(async () => {
        setLoading(true);
        const r = await api.get(`/my/bots/${botId}/learnings`);
        setItems(r?.learnings || r?.data || []);
        setLoading(false);
    }, [botId]);
    useEffect(() => { load(); }, [load]);

    const decide = async (id, decision) => {
        const r = await api.post(`/my/bots/${botId}/learnings/${id}/${decision}`);
        if (r?.error) return notify.error(r.error);
        notify.success(decision === 'approve' ? 'Ajouté à la base' : 'Rejeté');
        load();
    };

    return (
        <>
            <div className="card" style={{ marginBottom: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Apprentissages automatiques</h3>
                        <p className="card-subtitle">Q&R extraites automatiquement des conversations terminées (cron toutes les 15 min). Validez pour les ajouter à la base.</p>
                    </div>
                </div>
            </div>

            {loading ? <div style={{ textAlign: 'center', padding: '2rem' }}><Spinner /></div>
            : items.length === 0 ? <EmptyState icon={Icons.Sparkles} title="Aucun apprentissage en attente" desc="Quand le bot recevra des appels et que l'apprentissage automatique est activé, les Q&R extraites apparaîtront ici pour validation." />
            : <div className="vstack gap-sm">{items.map(l => (
                <div key={l.id} className="card-flat">
                    <div className="text-bold text-sm" style={{ marginBottom: '0.3rem' }}>{l.question}</div>
                    <div className="text-sm text-muted" style={{ marginBottom: '0.6rem' }}>{l.answer}</div>
                    <div className="hstack" style={{ justifyContent: 'space-between' }}>
                        <span className="text-xs text-muted">{fmtRelative(l.created_at)}</span>
                        <div className="hstack gap-sm">
                            <button className="btn btn-secondary btn-sm" onClick={() => decide(l.id, 'reject')}><Icons.X className="w-4 h-4" /> Rejeter</button>
                            <button className="btn btn-success btn-sm" onClick={() => decide(l.id, 'approve')}><Icons.Check className="w-4 h-4" /> Approuver</button>
                        </div>
                    </div>
                </div>
            ))}</div>}
        </>
    );
};

// ---------- Dataset ----------
const BotDatasetPanel = ({ botId }) => {
    const { notify } = useApp();
    return (
        <>
            <SoonBanner endpoint="POST /my/bots/:id/dataset/import">
                Importez un fichier CSV (colonnes <code>question,answer,category</code>) pour entraîner le bot en masse depuis vos données existantes (FAQ exportée, ticketing CRM, ancien chatbot).
            </SoonBanner>
            <div className="card">
                <div className="card-header">
                    <h3 className="card-title">Import CSV</h3>
                </div>
                <div className="card-body">
                    <div style={{ border: '2px dashed var(--border)', borderRadius: 'var(--radius-xl)', padding: '3rem', textAlign: 'center' }}>
                        <Icons.Upload className="w-10 h-10 text-muted" />
                        <div style={{ marginTop: '0.85rem', fontWeight: 700 }}>Glissez votre CSV ici</div>
                        <div className="text-sm text-muted" style={{ margin: '0.4rem 0 1rem' }}>Format attendu : 3 colonnes <code className="text-mono">question,answer,category</code> · max 5000 lignes / 5 MB</div>
                        <button className="btn btn-secondary" disabled>
                            <Icons.Upload className="w-4 h-4" /> Sélectionner un fichier (bientôt)
                        </button>
                    </div>
                </div>
            </div>

            <div className="card" style={{ marginTop: '1rem' }}>
                <div className="card-header"><h3 className="card-title">Historique d'import</h3></div>
                <div className="card-body text-sm text-muted text-center" style={{ padding: '2rem' }}>Aucun import pour l'instant.</div>
            </div>
        </>
    );
};

// ---------- Simulator (chat live) ----------
const BotSimulatorPanel = ({ bot }) => {
    const [messages, setMessages] = useState([
        { id: 1, role: 'system', text: `Simulator démarré · voix ${bot.voice} · langue ${(bot.language || 'fr').toUpperCase()}` },
        { id: 2, role: 'bot', text: bot.greeting || 'Bonjour, je vous écoute.' },
    ]);
    const [input, setInput] = useState('');
    const [sending, setSending] = useState(false);
    const [liveMode, setLiveMode] = useState(true);
    const bodyRef = useRef(null);

    useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [messages, sending]);

    const send = async () => {
        const text = input.trim();
        if (!text || sending) return;
        const userMsg = { id: Date.now(), role: 'user', text };
        setMessages(m => [...m, userMsg]);
        setInput('');
        setSending(true);

        const history = messages.filter(m => m.role !== 'system').map(m => ({ role: m.role === 'bot' ? 'assistant' : 'user', content: m.text }));

        let answered = false;
        if (liveMode) {
            const r = await api.post(`/my/bots/${bot.id}/simulate`, { message: text, history });
            if (!r?.error && (r.reply || r.message || r.text)) {
                setMessages(m => [...m, { id: Date.now() + 1, role: 'bot', text: r.reply || r.message || r.text, latency: r.latency_ms }]);
                answered = true;
            } else if (r?._status === 404 || r?._status === 0) {
                setLiveMode(false);
            } else if (r?.error) {
                setMessages(m => [...m, { id: Date.now() + 1, role: 'system', text: `Erreur: ${r.error}` }]);
                answered = true;
            }
        }

        if (!answered) {
            const ans = mockAnswer(bot, text);
            await new Promise(r => setTimeout(r, 600 + Math.random() * 600));
            setMessages(m => [...m, { id: Date.now() + 1, role: 'bot', text: ans, mock: true }]);
        }
        setSending(false);
    };

    const reset = () => setMessages([
        { id: 1, role: 'system', text: 'Conversation réinitialisée' },
        { id: 2, role: 'bot', text: bot.greeting || 'Bonjour, je vous écoute.' },
    ]);

    return (
        <>
            {!liveMode && (
                <SoonBanner endpoint="POST /my/bots/:id/simulate">
                    Mode démo activé : les réponses sont générées localement en piochant dans vos instructions et la base Q&R. Pour des réponses réelles via OpenAI, branchez l'endpoint côté <code>ch-vocal-api</code>.
                </SoonBanner>
            )}
            <div className="simulator">
                <div className="chat-shell">
                    <div className="chat-header">
                        <div className="chat-header-info">
                            <div className="bot-avatar" style={{ width: 36, height: 36, borderRadius: 10 }}><Icons.Robot className="w-4 h-4" /></div>
                            <div>
                                <div className="chat-header-title">{bot.name}</div>
                                <div className="chat-header-sub">
                                    <span className={`dot ${liveMode ? 'live' : 'draft'}`} style={{ marginRight: 4 }} />
                                    {liveMode ? 'Mode live' : 'Mode démo'} · {bot.voice}
                                </div>
                            </div>
                        </div>
                        <div className="hstack">
                            <button className="btn btn-ghost btn-sm" onClick={() => setLiveMode(v => !v)}>
                                {liveMode ? 'Mode démo' : 'Mode live'}
                            </button>
                            <button className="btn-icon btn btn-ghost" onClick={reset} title="Réinitialiser"><Icons.Refresh className="w-4 h-4" /></button>
                        </div>
                    </div>
                    <div className="chat-body" ref={bodyRef}>
                        {messages.map(m => (
                            <div key={m.id} className={`bubble ${m.role}`}>
                                {m.text}
                                {m.latency && <div className="bubble-meta">{m.latency} ms</div>}
                                {m.mock && <div className="bubble-meta">démo</div>}
                            </div>
                        ))}
                        {sending && <div className="chat-typing"><span /><span /><span /></div>}
                    </div>
                    <div className="chat-composer">
                        <textarea
                            placeholder="Tapez ce que dirait l'appelant…"
                            value={input}
                            onChange={e => setInput(e.target.value)}
                            onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } }}
                            rows={1}
                        />
                        <button className="btn btn-primary btn-icon" onClick={send} disabled={!input.trim() || sending}>
                            <Icons.PaperPlane className="w-4 h-4" />
                        </button>
                    </div>
                </div>

                <div className="simulator-side">
                    <div className="card-flat">
                        <div className="text-xs text-bold text-muted" style={{ marginBottom: '0.5rem', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Contexte simulé</div>
                        <div className="vstack gap-sm text-sm">
                            <div><span className="text-muted">Voix :</span> <span className="text-bold">{bot.voice}</span></div>
                            <div><span className="text-muted">Langue :</span> <span className="text-bold">{(bot.language || 'fr').toUpperCase()}</span></div>
                            <div><span className="text-muted">Transfert :</span> <span className="text-bold">{bot.human_transfer_number || '—'}</span></div>
                            <div><span className="text-muted">RDV :</span> <span className="text-bold">{bot.appointments_enabled ? 'Activé' : 'Non'}</span></div>
                        </div>
                    </div>

                    <div className="card-flat">
                        <div className="text-xs text-bold text-muted" style={{ marginBottom: '0.5rem', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Scénarios suggérés</div>
                        <div className="vstack gap-sm">
                            {[
                                'Bonjour, j\'aimerais prendre rendez-vous.',
                                'Quels sont vos horaires d\'ouverture ?',
                                'Combien coûte une consultation ?',
                                'Je veux annuler mon rendez-vous de demain.',
                                'Pouvez-vous me passer quelqu\'un, c\'est urgent.',
                            ].map((s, i) => (
                                <button key={i} className="btn btn-ghost btn-sm" style={{ justifyContent: 'flex-start', textAlign: 'left' }} onClick={() => setInput(s)}>{s}</button>
                            ))}
                        </div>
                    </div>

                    <div className="card-flat" style={{ background: 'linear-gradient(135deg, #fdfdff, #f5f3ff)' }}>
                        <div className="text-xs text-bold" style={{ color: 'var(--primary-dark)', marginBottom: '0.4rem', display: 'inline-flex', alignItems: 'center', gap: '0.35rem' }}><Icons.Lightbulb className="w-4 h-4" /> Astuce</div>
                        <div className="text-xs text-muted">Le simulator utilise EXACTEMENT le même prompt et la même base Q&A que ce qui sera utilisé en production. Si une réponse est mauvaise ici, elle sera mauvaise au téléphone.</div>
                    </div>
                </div>
            </div>
        </>
    );
};

function mockAnswer(bot, text) {
    const t = text.toLowerCase();
    if (/horaire|ouvert|fermé/.test(t)) return 'Nous sommes ouverts du lundi au vendredi de 8h à 18h, et le samedi de 9h à 12h. Souhaitez-vous prendre rendez-vous ?';
    if (/rendez|rdv|réserv/.test(t)) return bot.appointments_enabled ? 'Bien sûr, pour quelle date et combien de personnes ? (mode démo)' : 'La prise de rendez-vous n\'est pas encore activée pour ce bot.';
    if (/prix|tarif|coûte|combien/.test(t)) return 'Les tarifs varient selon la prestation. Souhaitez-vous que je vous transfère à un conseiller pour un devis personnalisé ?';
    if (/annul/.test(t)) return 'Pour annuler, j\'ai besoin de votre nom et de la date du rendez-vous, s\'il vous plaît.';
    if (/urgen|humain|quelqu/.test(t)) return bot.human_transfer_number ? `Je vous transfère immédiatement à ${bot.human_transfer_number}. Un instant…` : 'Je transfère votre appel à un opérateur humain, un instant…';
    if (/bonjour|salut|hello/.test(t)) return 'Bonjour ! Comment puis-je vous aider aujourd\'hui ?';
    return 'Je ne suis pas sûr d\'avoir compris. Pouvez-vous reformuler votre demande ? (réponse de démonstration)';
}

// ---------- A/B Test ----------
const BotABTestPanel = ({ botId }) => {
    return (
        <>
            <SoonBanner endpoint="GET /my/bots/:id/abtests, POST /my/bots/:id/abtests/:tid/promote">
                Comparez 2 versions de prompt en répartissant le trafic 50/50, mesurez le taux de résolution, NPS et durée moyenne. Promouvez la gagnante en 1 clic.
            </SoonBanner>

            <div className="card" style={{ marginBottom: '1rem' }}>
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Test en cours · "Greeting plus court"</h3>
                        <p className="card-subtitle">Démarré il y a 4 jours · 247 appels collectés sur 500 ciblés</p>
                    </div>
                    <button className="btn btn-secondary btn-sm" disabled>Arrêter le test</button>
                </div>
                <div className="card-body">
                    <div className="abtest-grid">
                        <div className="variant-card">
                            <div className="variant-header">
                                <div>
                                    <div className="variant-name">Variante A — "Bonjour, restaurant Le Lac, je vous écoute !"</div>
                                    <div className="text-xs text-muted">Version actuelle · 50% du trafic</div>
                                </div>
                                <span className="badge badge-gray">Contrôle</span>
                            </div>
                            <div className="variant-bar"><div className="variant-bar-fill" style={{ width: '50%' }} /></div>
                            <div className="variant-stats">
                                <div><div className="variant-stat-label">Appels</div><div className="variant-stat-value">123</div></div>
                                <div><div className="variant-stat-label">Résolution</div><div className="variant-stat-value">68%</div></div>
                                <div><div className="variant-stat-label">NPS</div><div className="variant-stat-value">+42</div></div>
                            </div>
                        </div>
                        <div className="variant-card winner">
                            <div className="variant-header">
                                <div>
                                    <div className="variant-name">Variante B — "Le Lac, j'écoute !"</div>
                                    <div className="text-xs text-muted">Version testée · 50% du trafic</div>
                                </div>
                                <span className="badge badge-success">+12% résolution</span>
                            </div>
                            <div className="variant-bar"><div className="variant-bar-fill" style={{ width: '50%', background: 'linear-gradient(90deg, var(--success), #16a34a)' }} /></div>
                            <div className="variant-stats">
                                <div><div className="variant-stat-label">Appels</div><div className="variant-stat-value">124</div></div>
                                <div><div className="variant-stat-label">Résolution</div><div className="variant-stat-value">76%</div></div>
                                <div><div className="variant-stat-label">NPS</div><div className="variant-stat-value">+51</div></div>
                            </div>
                        </div>
                    </div>
                    <div className="hstack" style={{ justifyContent: 'flex-end', marginTop: '1rem' }}>
                        <button className="btn btn-success" disabled>
                            <Icons.Check className="w-4 h-4" /> Promouvoir la variante B
                        </button>
                    </div>
                </div>
            </div>

            <div className="card">
                <div className="card-header">
                    <h3 className="card-title">Lancer un nouveau test</h3>
                </div>
                <div className="card-body">
                    <button className="btn btn-primary" disabled>
                        <Icons.Plus className="w-4 h-4" /> Créer un test A/B (bientôt)
                    </button>
                </div>
            </div>
        </>
    );
};

// ---------- Conversations ----------
const BotConversationsPanel = ({ botId }) => {
    const { notify } = useApp();
    const [convos, setConvos] = useState(null);

    useEffect(() => {
        api.get(`/my/bots/${botId}/conversations`).then(r => {
            setConvos(r?.conversations || r?.data || []);
        });
    }, [botId]);

    return (
        <>
            <div className="card">
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Conversations récentes</h3>
                        <p className="card-subtitle">Transcriptions des derniers appels traités par ce bot.</p>
                    </div>
                </div>
                <div style={{ overflow: 'auto' }}>
                    {convos === null ? <div style={{ textAlign: 'center', padding: '2rem' }}><Spinner /></div>
                    : convos.length === 0 ? <EmptyState icon={Icons.Phone2} title="Aucune conversation" desc="Quand votre bot recevra des appels, ils apparaîtront ici avec la transcription complète et le score de qualité." />
                    : <table className="data-table">
                        <thead><tr><th>Appelant</th><th>Date</th><th>Durée</th><th>Statut</th><th>Résumé</th><th></th></tr></thead>
                        <tbody>{convos.map(c => (
                            <tr key={c.id}>
                                <td className="text-mono text-sm">{c.from_number || c.caller || '—'}</td>
                                <td className="text-sm">{fmtDateTime(c.started_at || c.created_at)}</td>
                                <td className="text-sm">{fmtDuration(c.duration_seconds || c.duration)}</td>
                                <td><span className="badge badge-gray">{c.status || 'completed'}</span></td>
                                <td className="text-sm" style={{ maxWidth: 400 }}>{c.summary || '—'}</td>
                                <td><button className="btn-icon btn btn-ghost"><Icons.External className="w-4 h-4" /></button></td>
                            </tr>
                        ))}</tbody>
                    </table>}
                </div>
            </div>
        </>
    );
};

// ---------- Appointments ----------
const BotAppointmentsPanel = ({ form, setForm, saving, onSave }) => (
    <div className="card">
        <div className="card-header">
            <div>
                <h3 className="card-title">Prise de rendez-vous</h3>
                <p className="card-subtitle">Le bot peut réserver directement dans votre agenda Google Calendar / Outlook (à connecter).</p>
            </div>
            <button className="btn btn-primary btn-sm" onClick={() => onSave()} disabled={saving}>
                {saving ? 'Enregistrement…' : 'Enregistrer'}
            </button>
        </div>
        <div className="card-body">
            <label className="toggle-row" style={{ marginBottom: '1rem' }}>
                <input type="checkbox" checked={form.appointments_enabled} onChange={e => setForm(f => ({ ...f, appointments_enabled: e.target.checked }))} />
                <span>Activer la prise de rendez-vous par le bot</span>
            </label>

            <div className="form-row row-grid">
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Durée d'un RDV (min)</label>
                    <input type="number" min="5" max="240" disabled={!form.appointments_enabled} value={form.appointment_duration_minutes} onChange={e => setForm(f => ({ ...f, appointment_duration_minutes: parseInt(e.target.value || '30') }))} />
                </div>
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Tampon entre RDV (min)</label>
                    <input type="number" min="0" max="60" disabled={!form.appointments_enabled} value={form.appointment_buffer_minutes} onChange={e => setForm(f => ({ ...f, appointment_buffer_minutes: parseInt(e.target.value || '0') }))} />
                </div>
            </div>
            <div className="form-row row-grid">
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Horaires (JSON ou texte libre)</label>
                    <textarea rows={3} disabled={!form.appointments_enabled} value={form.appointment_business_hours} onChange={e => setForm(f => ({ ...f, appointment_business_hours: e.target.value }))} placeholder='{"mon-fri": "09:00-18:00", "sat": "09:00-12:00"}' className="text-mono text-sm" />
                </div>
                <div className="form-row" style={{ marginBottom: 0 }}>
                    <label>Email de notification</label>
                    <input type="email" disabled={!form.appointments_enabled} value={form.appointment_notify_email} onChange={e => setForm(f => ({ ...f, appointment_notify_email: e.target.value }))} placeholder="rdv@entreprise.ch" />
                    <span className="form-help">Recevez un email à chaque réservation prise par le bot.</span>
                </div>
            </div>
            <div className="form-row" style={{ maxWidth: 320 }}>
                <label>Réserver jusqu'à (jours d'avance)</label>
                <input type="number" min="1" max="180" disabled={!form.appointments_enabled} value={form.appointment_max_days_ahead} onChange={e => setForm(f => ({ ...f, appointment_max_days_ahead: parseInt(e.target.value || '30') }))} />
            </div>
        </div>
    </div>
);

// ---------- Leads ----------
const BotLeadsPanel = ({ form, setForm, saving, onSave }) => (
    <div className="card">
        <div className="card-header">
            <div>
                <h3 className="card-title">Lead scoring (BANT)</h3>
                <p className="card-subtitle">Le bot pose subtilement des questions de qualification et score chaque appel.</p>
            </div>
            <button className="btn btn-primary btn-sm" onClick={() => onSave()} disabled={saving}>
                {saving ? 'Enregistrement…' : 'Enregistrer'}
            </button>
        </div>
        <div className="card-body">
            <label className="toggle-row" style={{ marginBottom: '1rem' }}>
                <input type="checkbox" checked={form.lead_scoring_enabled} onChange={e => setForm(f => ({ ...f, lead_scoring_enabled: e.target.checked }))} />
                <span>Activer le lead scoring sur ce bot</span>
            </label>
            <div className="form-row">
                <label>Prompt de qualification</label>
                <textarea
                    className="form-input code-textarea"
                    rows={10}
                    disabled={!form.lead_scoring_enabled}
                    value={form.lead_scoring_prompt}
                    onChange={e => setForm(f => ({ ...f, lead_scoring_prompt: e.target.value }))}
                    placeholder="Au cours de la conversation, identifie subtilement : le BUDGET (B), l'AUTORITÉ de décision (A), le BESOIN (N) et le TIMING (T). Score chaque dimension de 0 à 5 et calcule un score global."
                />
            </div>
        </div>
    </div>
);

// ---------- Lines ----------
const BotLinesPanel = ({ botId }) => {
    const [lines, setLines] = useState(null);
    useEffect(() => {
        api.get('/my/lines').then(r => {
            const all = r?.lines || r?.data || [];
            setLines(all.filter(l => l.bot_id === botId || l.voice_bot_id === botId));
        });
    }, [botId]);

    return (
        <>
            <div className="card">
                <div className="card-header">
                    <div>
                        <h3 className="card-title">Lignes téléphoniques connectées</h3>
                        <p className="card-subtitle">Quels numéros routent vers ce bot ?</p>
                    </div>
                    <a href={`${CONFIG.MY_URL}#lines`} target="_blank" rel="noopener" className="btn btn-secondary btn-sm">
                        <Icons.External className="w-4 h-4" /> Gérer mes lignes
                    </a>
                </div>
                <div style={{ overflow: 'auto' }}>
                    {lines === null ? <div style={{ textAlign: 'center', padding: '2rem' }}><Spinner /></div>
                    : lines.length === 0 ? <EmptyState icon={Icons.Headset} title="Aucune ligne ne route vers ce bot" desc="Allez dans my.vocal.ch › Lignes › sélectionnez une ligne › type 'Bot vocal IA' et choisissez ce bot." />
                    : <table className="data-table">
                        <thead><tr><th>Numéro</th><th>Type</th><th>Statut</th><th></th></tr></thead>
                        <tbody>{lines.map(l => (
                            <tr key={l.id}>
                                <td className="text-mono">{l.number}</td>
                                <td><span className="badge badge-primary">{l.type}</span></td>
                                <td><span className={`badge ${l.is_active ? 'badge-success' : 'badge-gray'}`}>{l.is_active ? 'Actif' : 'Inactif'}</span></td>
                                <td><a href={`${CONFIG.MY_URL}#lines`} target="_blank" rel="noopener" className="btn-icon btn btn-ghost"><Icons.External className="w-4 h-4" /></a></td>
                            </tr>
                        ))}</tbody>
                    </table>}
                </div>
            </div>
        </>
    );
};

// ---------- Versions ----------
const BotVersionsPanel = ({ botId }) => {
    const fakeVersions = [
        { id: 12, label: 'v12', current: true, author: 'vous', date: new Date().toISOString(), summary: 'Ajout du transfert vers +41 21 ... pour les urgences.' },
        { id: 11, label: 'v11', author: 'vous', date: new Date(Date.now() - 86400000 * 2).toISOString(), summary: 'Greeting raccourci, ajout question allergie.' },
        { id: 10, label: 'v10', author: 'admin', date: new Date(Date.now() - 86400000 * 7).toISOString(), summary: 'Mise à jour KB tarifs.' },
        { id: 9,  label: 'v9',  author: 'vous', date: new Date(Date.now() - 86400000 * 14).toISOString(), summary: 'Activation lead scoring.' },
        { id: 8,  label: 'v8',  author: 'vous', date: new Date(Date.now() - 86400000 * 30).toISOString(), summary: 'Création du bot depuis template Restaurant.' },
    ];
    return (
        <>
            <SoonBanner endpoint="GET /my/bots/:id/versions, POST /my/bots/:id/versions/:vid/rollback">
                Le versioning enregistre une snapshot du prompt + KB + voix à chaque modification. Vous pouvez comparer 2 versions et revenir à une version antérieure en 1 clic.
            </SoonBanner>
            <div className="card">
                <div className="card-header"><h3 className="card-title">Historique des versions</h3></div>
                <div>
                    {fakeVersions.map(v => (
                        <div key={v.id} className={`version-row ${v.current ? 'current' : ''}`}>
                            <span className="version-dot">{v.label}</span>
                            <div className="version-info">
                                <div className="version-title">{v.summary}</div>
                                <div className="version-meta">par {v.author} · {fmtRelative(v.date)}</div>
                            </div>
                            <div className="version-actions">
                                {v.current
                                    ? <span className="badge badge-success">Actuelle</span>
                                    : <>
                                        <button className="btn btn-ghost btn-sm" disabled>Diff</button>
                                        <button className="btn btn-secondary btn-sm" disabled>Revenir ici</button>
                                    </>
                                }
                            </div>
                        </div>
                    ))}
                </div>
            </div>
        </>
    );
};

// ---------- Analytics ----------
const BotAnalyticsPanel = ({ botId }) => (
    <>
        <SoonBanner endpoint="GET /my/bots/:id/analytics?range=30d">
            KPIs détaillés par bot : volume d'appels, intents top 10, taux de résolution, escalade humaine, NPS, coût IA estimé, durée moyenne.
        </SoonBanner>
        <div className="kpi-grid">
            <KpiCard label="Appels (30j)" value="—" />
            <KpiCard label="Durée moyenne" value="—" />
            <KpiCard label="Taux de résolution" value="—" />
            <KpiCard label="Escalade humaine" value="—" />
            <KpiCard label="NPS moyen" value="—" />
            <KpiCard label="Coût OpenAI" value="— CHF" />
        </div>
        <div className="card-grid cols-2">
            <div className="card">
                <div className="card-header"><h3 className="card-title">Top intents</h3></div>
                <div className="card-body text-sm text-muted text-center" style={{ padding: '2rem' }}>Données à brancher.</div>
            </div>
            <div className="card">
                <div className="card-header"><h3 className="card-title">Volume par jour</h3></div>
                <div className="card-body text-sm text-muted text-center" style={{ padding: '2rem' }}>Graphique à brancher.</div>
            </div>
        </div>
    </>
);

/* ============================================================================
   APP ROOT
   ============================================================================ */

const Shell = () => {
    const { user, route } = useApp();
    const [currentBot, setCurrentBot] = useState(null);

    useEffect(() => {
        if (route.view !== 'bot-detail') setCurrentBot(null);
    }, [route.view, route.botId]);

    if (!user) return <LoginScreen />;

    return (
        <div className="app-layout">
            <Sidebar currentBot={currentBot} />
            <main className="main-content">
                {route.view === 'bots'        && <BotsListView />}
                {route.view === 'templates'   && <TemplatesView />}
                {route.view === 'marketplace' && <MarketplaceView />}
                {route.view === 'analytics'   && <GlobalAnalyticsView />}
                {route.view === 'account'     && <AccountSettingsView />}
                {route.view === 'bot-detail'  && (
                    <BotDetailView
                        key={route.botId}
                        botId={route.botId}
                        panel={route.panel}
                        onBotLoaded={setCurrentBot}
                    />
                )}
            </main>
        </div>
    );
};

const App = () => (
    <AppProvider>
        <Shell />
    </AppProvider>
);

const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);
