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

const CONFIG = { API_URL: 'https://api.helvia.app', MCP_URL: 'https://mcp.helvia.app' };

const LINE_TYPES = {
    forward: { label: 'Redirection', color: 'badge-info' },
    ivr: { label: 'Menu IVR', color: 'badge-warning' },
    voice_ai: { label: 'IA Vocale', color: 'badge-primary' },
    voicemail: { label: 'Messagerie', color: 'badge-gray' },
    browser: { label: 'Navigateur', color: 'badge-success' },
    link: { label: 'Envoi de lien', color: 'badge-purple' },
    webhook: { label: 'Webhook', color: 'badge-orange' },
    external_api: { label: 'API externe', color: 'badge-dark' },
};

// Parse rapide d'un User-Agent (sans dépendance externe).
function parseUserAgent(ua) {
    if (!ua) return '—';
    const u = String(ua);
    let browser = 'Inconnu', version = '';
    if (/Edg\//.test(u))                          { browser = 'Edge';    version = (u.match(/Edg\/([\d.]+)/) || [])[1] || ''; }
    else if (/OPR\//.test(u) || /Opera/.test(u))  { browser = 'Opera';   version = (u.match(/OPR\/([\d.]+)/) || [])[1] || ''; }
    else if (/Chrome\//.test(u))                  { browser = 'Chrome';  version = (u.match(/Chrome\/([\d.]+)/) || [])[1] || ''; }
    else if (/Safari\//.test(u))                  { browser = 'Safari';  version = (u.match(/Version\/([\d.]+)/) || [])[1] || ''; }
    else if (/Firefox\//.test(u))                 { browser = 'Firefox'; version = (u.match(/Firefox\/([\d.]+)/) || [])[1] || ''; }
    let os = 'Inconnu';
    if (/Windows NT 10/.test(u)) os = 'Windows 10/11';
    else if (/Windows/.test(u))  os = 'Windows';
    else if (/Mac OS X/.test(u)) os = 'macOS ' + ((u.match(/Mac OS X ([\d_]+)/) || [])[1] || '').replace(/_/g, '.');
    else if (/Android/.test(u))  os = 'Android ' + ((u.match(/Android ([\d.]+)/) || [])[1] || '');
    else if (/iPhone|iPad/.test(u)) os = 'iOS ' + ((u.match(/OS ([\d_]+)/) || [])[1] || '').replace(/_/g, '.');
    else if (/Linux/.test(u))    os = 'Linux';
    const device = /Mobile|Android|iPhone/.test(u) ? 'Mobile' : 'Desktop';
    return `${browser}${version ? ' ' + version.split('.')[0] : ''} · ${os} · ${device}`;
}

function formatDate(dateStr) {
    if (!dateStr) return '-';
    return new Date(dateStr).toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', year: 'numeric' });
}

function formatCurrency(amount, currency = 'CHF') {
    if (amount === null || amount === undefined) return '-';
    return `${Number(amount).toFixed(2)} ${currency}`;
}

// ==================== ICONS ====================
const Icons = {
    Phone: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    PhoneIncoming: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M21 3l-6 6m0 0V4m0 5h5M3 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" />
        </svg>
    ),
    Users: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
        </svg>
    ),
    CreditCard: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
        </svg>
    ),
    Key: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
        </svg>
    ),
    Shield: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
        </svg>
    ),
    HeadphonesIcon: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
        </svg>
    ),
    BarChart: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Settings: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Plus: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
        </svg>
    ),
    X: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
        </svg>
    ),
    ChevronRight: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
        </svg>
    ),
    ChevronLeft: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
        </svg>
    ),
    ChevronDown: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
        </svg>
    ),
    ChevronUp: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M5 15l7-7 7 7" />
        </svg>
    ),
    ArrowRight: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3" />
        </svg>
    ),
    Download: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V4" />
        </svg>
    ),
    Wallet: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M21 12V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2h14a2 2 0 002-2v-3m-4-1a2 2 0 110-4h4v4h-4z" />
        </svg>
    ),
    LogOut: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Search: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
        </svg>
    ),
    Edit: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Trash: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Mail: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    AlertTriangle: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
        </svg>
    ),
    Bell: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 17h5l-1.4-1.4A2 2 0 0118 14.2V11a6 6 0 10-12 0v3.2c0 .53-.21 1.04-.59 1.41L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
        </svg>
    ),
    Refresh: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Menu: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
        </svg>
    ),
    Home: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Link: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
        </svg>
    ),
    Globe: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5a17.92 17.92 0 0 1-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
        </svg>
    ),
    Server: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
        </svg>
    ),
    Eye: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
        </svg>
    ),
    MessageSquare: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
        </svg>
    ),
    Send: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    ExternalLink: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    EyeOff: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
        </svg>
    ),
    Check: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
        </svg>
    ),
    Copy: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2" strokeLinecap="round" strokeLinejoin="round" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
        </svg>
    ),
    Gift: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V6a2 2 0 10-2 2h2zm-7 5h14M5 13a2 2 0 110-4h14a2 2 0 110 4M5 13v7a2 2 0 002 2h10a2 2 0 002-2v-7" />
        </svg>
    ),
    LogIn: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
        </svg>
    ),
    Sparkles: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3zM18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8L18 14zM6 14l.8 2.2L9 17l-2.2.8L6 20l-.8-2.2L3 17l2.2-.8L6 14z" />
        </svg>
    ),
    TrendingUp: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M3 17l6-6 4 4 8-8m0 0h-5m5 0v5" />
        </svg>
    ),
    DollarSign: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m6-12a4 4 0 00-4-4h-4a3 3 0 000 6h4a3 3 0 010 6h-4a4 4 0 01-4-4" />
        </svg>
    ),
    Activity: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M22 12h-4l-3 9L9 3l-3 9H2" />
        </svg>
    ),
    Code: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M16 18l6-6-6-6M8 6l-6 6 6 6" />
        </svg>
    ),
    RefreshCw: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M4 4v6h6M20 20v-6h-6M5 9a7 7 0 0112-5l3 3M19 15a7 7 0 01-12 5l-3-3" />
        </svg>
    ),
    Terminal: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M4 17l6-6-6-6M12 19h8" />
        </svg>
    ),
    Lightbulb: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M9 18h6M10 22h4M12 2a7 7 0 00-4 12.7c.7.6 1 1.5 1 2.3v1h6v-1c0-.8.3-1.7 1-2.3A7 7 0 0012 2z" />
        </svg>
    ),
    PieChart: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z" />
        </svg>
    ),
    Clock: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
        </svg>
    ),
    Calendar: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <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" />
        </svg>
    ),
    Bot: ({ className = "w-5 h-5" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <rect x="4" y="8" width="16" height="12" rx="2" strokeLinecap="round" strokeLinejoin="round" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v4M9 14h.01M15 14h.01M9 18h6" />
        </svg>
    ),
};

// ==================== SKELETON ====================
const Skeleton = ({ width, height, className = '', style = {}, circle = false }) => (
    <span
        className={`skeleton ${circle ? 'skeleton-circle' : ''} ${className}`}
        style={{
            width: typeof width === 'number' ? `${width}px` : (width || '100%'),
            height: typeof height === 'number' ? `${height}px` : (height || '0.85rem'),
            ...style,
        }}
    >&nbsp;</span>
);

const SkeletonStatsGrid = ({ count = 4 }) => (
    <div className="skeleton-stats-grid">
        {Array.from({ length: count }).map((_, i) => (
            <div key={i} className="skeleton-stat-card">
                <Skeleton width={48} height={48} circle />
                <div style={{ flex: 1 }}>
                    <Skeleton width="55%" height={10} style={{ marginBottom: '0.5rem' }} />
                    <Skeleton width="40%" height={20} />
                </div>
            </div>
        ))}
    </div>
);

const SkeletonTable = ({ rows = 8, cols = 6, hasHeader = true }) => (
    <div className="table-container">
        <table className="skeleton-table">
            {hasHeader && (
                <thead>
                    <tr>
                        {Array.from({ length: cols }).map((_, c) => (
                            <th key={c} style={{ padding: '0.75rem 1rem', background: '#f8fafc', borderBottom: '1px solid var(--border)' }}>
                                <Skeleton width={`${50 + (c * 7) % 30}%`} height={9} />
                            </th>
                        ))}
                    </tr>
                </thead>
            )}
            <tbody>
                {Array.from({ length: rows }).map((_, r) => (
                    <tr key={r}>
                        {Array.from({ length: cols }).map((_, c) => (
                            <td key={c}>
                                <Skeleton width={`${40 + ((r + c * 13) % 50)}%`} height={11} />
                            </td>
                        ))}
                    </tr>
                ))}
            </tbody>
        </table>
    </div>
);

const SkeletonCards = ({ count = 6, columns = 3 }) => (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gap: '1rem' }}>
        {Array.from({ length: count }).map((_, i) => (
            <div key={i} className="card" style={{ padding: '1.25rem' }}>
                <Skeleton width="60%" height={14} style={{ marginBottom: '0.75rem' }} />
                <Skeleton width="100%" height={10} style={{ marginBottom: '0.4rem' }} />
                <Skeleton width="80%" height={10} />
            </div>
        ))}
    </div>
);

// Debounce a value for search inputs
const useDebouncedValue = (value, delay = 300) => {
    const [debounced, setDebounced] = useState(value);
    useEffect(() => {
        const t = setTimeout(() => setDebounced(value), delay);
        return () => clearTimeout(t);
    }, [value, delay]);
    return debounced;
};

// ==================== API SERVICE ====================
const api = {
    token: null,
    async req(path, options = {}) {
        const headers = { 'Content-Type': 'application/json', ...options.headers };
        if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
        const resp = await fetch(`${CONFIG.API_URL}${path}`, { ...options, headers });
        return resp.json();
    },
    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' }); },
    async upload(path, formData) {
        const headers = {};
        if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
        const resp = await fetch(`${CONFIG.API_URL}${path}`, { method: 'POST', headers, body: formData });
        return resp.json();
    },
};

// API dédiée au worker MCP (mcp.helvia.app) — réutilise le token de session admin
const mcpApi = {
    async req(path, options = {}) {
        const headers = { 'Content-Type': 'application/json', ...options.headers };
        if (api.token) headers['Authorization'] = `Bearer ${api.token}`;
        const resp = await fetch(`${CONFIG.MCP_URL}${path}`, { ...options, headers });
        return resp.json();
    },
    get(path) { return this.req(path); },
    post(path, body) { return this.req(path, { method: 'POST', body: JSON.stringify(body) }); },
    patch(path, body) { return this.req(path, { method: 'PATCH', 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.875rem 1.25rem;border-radius:10px;font-size:0.875rem;font-weight:500;
            color:#fff;box-shadow:0 8px 24px rgba(0,0,0,0.18);
            transform:translateY(0);opacity:1;transition:all 0.3s ease;
            max-width:360px;font-family:Inter,sans-serif;
        `;
        const colors = { success: '#16a34a', error: '#dc2626', info: '#6366f1' };
        el.style.background = colors[type] || colors.info;
        el.textContent = message;
        document.body.appendChild(el);
        setTimeout(() => {
            el.style.opacity = '0';
            el.style.transform = 'translateY(12px)';
            setTimeout(() => el.remove(), 300);
        }, 4000);
    }, []);
    return useMemo(() => ({
        success: (m) => show(m, 'success'),
        error: (m) => show(m, 'error'),
        info: (m) => show(m, 'info'),
    }), [show]);
};

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

const AppProvider = ({ children }) => {
    const [user, setUser] = useState(() => {
        const saved = localStorage.getItem('vocal_adm_user');
        const token = localStorage.getItem('vocal_adm_token');
        if (saved && token) { api.token = token; return { ...JSON.parse(saved), token }; }
        return null;
    });
    const [view, setView] = useState('dashboard');
    const [sidebarOpen, setSidebarOpen] = useState(false);
    const [selectedLine, setSelectedLine] = useState(null);
    const [selectedClient, setSelectedClient] = useState(null);

    const notify = useNotifications();

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

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

    const navigate = useCallback((v) => {
        setView(v);
        setSidebarOpen(false);
    }, []);

    const value = useMemo(() => ({
        user, view, sidebarOpen, setSidebarOpen,
        selectedLine, setSelectedLine,
        selectedClient, setSelectedClient,
        login, logout, navigate, notify,
    }), [user, view, sidebarOpen, selectedLine, selectedClient, login, logout, navigate, 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é, entrez le code manuellement'); }
            })
            .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 resp = await fetch(`${CONFIG.API_URL}/auth/request-code`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email }),
            });
            const data = await resp.json();
            if (data.success) setStep('code');
            else setError(data.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 resp = await fetch(`${CONFIG.API_URL}/auth/verify-code`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email, code }),
            });
            const data = await resp.json();
            if (data.success && data.token) login(data.user || { email }, data.token);
            else setError(data.error || 'Code invalide');
        } catch (e) { setError('Erreur réseau'); }
        finally { setLoading(false); }
    };

    const features = [
        { icon: '📞', title: 'Gestion des lignes', desc: 'Administrez toutes les lignes téléphoniques' },
        { icon: '📊', title: 'Suivi des appels', desc: 'Statistiques et analyses en temps réel' },
        { icon: '🤖', title: 'IA Vocale', desc: 'Assistants vocaux intelligents' },
        { icon: '🎧', title: 'Support intégré', desc: 'Gestion des tickets et assistance' },
    ];

    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" style={{ height: '36px' }} />
                    </div>

                    {step === 'email' ? (
                        <form onSubmit={handleSendCode} className="login-form">
                            <h1 className="login-title" style={{ textAlign: 'center', marginBottom: '0.5rem' }}>Bienvenue</h1>
                            <p style={{ textAlign: 'center', color: 'var(--text-muted)', fontSize: '0.875rem', marginBottom: '2rem' }}>
                                Connectez-vous à l'espace d'administration
                            </p>
                            <div className="form-group">
                                <label className="form-label">Adresse email</label>
                                <input type="email" value={email} onChange={(e) => setEmail(e.target.value)}
                                    placeholder="admin@helvia.app" className="form-input" autoFocus />
                            </div>
                            {error && <p style={{ color: '#dc2626', fontSize: '0.875rem', margin: '0 0 1rem' }}>{error}</p>}
                            <button type="submit" disabled={loading || !email} className="btn btn-primary" style={{ width: '100%' }}>
                                {loading ? 'Envoi...' : 'Recevoir le code'}
                            </button>
                        </form>
                    ) : (
                        <form onSubmit={handleVerify} className="login-form">
                            <h1 className="login-title" style={{ textAlign: 'center', marginBottom: '0.5rem' }}>Vérification</h1>
                            <p style={{ textAlign: 'center', color: 'var(--text-muted)', fontSize: '0.875rem', marginBottom: '2rem' }}>
                                Code envoyé à <strong>{email}</strong>
                            </p>
                            <div className="form-group" style={{ textAlign: 'center' }}>
                                <input type="text" value={code} onChange={(e) => setCode(e.target.value)}
                                    placeholder="000000" className="form-code-input" maxLength="6" autoFocus />
                            </div>
                            {error && <p style={{ color: '#dc2626', fontSize: '0.875rem', margin: '0 0 1rem', textAlign: 'center' }}>{error}</p>}
                            <button type="submit" disabled={loading || code.length < 4} className="btn btn-primary" style={{ width: '100%', marginBottom: '0.75rem' }}>
                                {loading ? 'Vérification...' : 'Vérifier'}
                            </button>
                            <button type="button" onClick={() => { setStep('email'); setCode(''); setError(''); }} className="btn btn-secondary" style={{ width: '100%' }}>
                                Retour
                            </button>
                        </form>
                    )}
                </div>
            </div>

            <div className="login-visual-side">
                <div className="login-visual-content">
                    <h2>Vocal Admin</h2>
                    <p style={{ marginBottom: '2.5rem' }}>Plateforme d'administration de votre téléphonie cloud</p>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', textAlign: 'left' }}>
                        {features.map((f, i) => (
                            <div key={i} style={{ background: 'rgba(255,255,255,0.12)', borderRadius: '12px', padding: '1rem' }}>
                                <div style={{ fontSize: '1.5rem', marginBottom: '0.5rem' }}>{f.icon}</div>
                                <div style={{ fontWeight: 600, fontSize: '0.875rem', marginBottom: '0.25rem' }}>{f.title}</div>
                                <div style={{ fontSize: '0.75rem', opacity: 0.8 }}>{f.desc}</div>
                            </div>
                        ))}
                    </div>
                </div>
            </div>
        </div>
    );
};

// ==================== SIDEBAR ====================
const Sidebar = () => {
    const { user, view, sidebarOpen, setSidebarOpen, navigate, logout } = useApp();

    const navSections = [
        {
            title: 'PRINCIPAL',
            items: [
                { id: 'dashboard', label: 'Dashboard', icon: Icons.Home },
                { id: 'analytics-platform', label: 'Analytics', icon: Icons.PieChart },
            ],
        },
        {
            title: 'GESTION',
            items: [
                { id: 'clients', label: 'Clients', icon: Icons.Users },
                { id: 'lines', label: 'Lignes', icon: Icons.Phone },
                { id: 'calls', label: 'Appels', icon: Icons.PhoneIncoming },
                { id: 'demandes', label: 'Demandes', icon: Icons.Refresh },
                { id: 'sms', label: 'SMS', icon: Icons.Send },
                { id: 'whatsapp', label: 'WhatsApp', icon: Icons.MessageSquare },
                { id: 'subscriptions', label: 'Abonnements', icon: Icons.CreditCard },
                { id: 'modules', label: 'Modules', icon: Icons.CreditCard },
                { id: 'partners', label: 'Partenaires', icon: Icons.Link },
                { id: 'api', label: 'API', icon: Icons.Key },
            ],
        },
        {
            title: 'SYSTÈME',
            items: [
                { id: 'twilio', label: 'Comptes Twilio', icon: Icons.Server },
                { id: 'settings', label: 'Paramètres', icon: Icons.Settings },
                { id: 'access', label: 'Access', icon: Icons.Key },
                { id: 'admins', label: 'Administrateurs', icon: Icons.Shield },
                { id: 'analytics', label: 'Analytics IA', icon: Icons.Sparkles },
                { id: 'mcp-keys', label: 'MCP Keys', icon: Icons.Code },
                { id: 'diagnostic', label: 'Diagnostic', icon: Icons.AlertTriangle },
                { id: 'support', label: 'Support', icon: Icons.HeadphonesIcon },
            ],
        },
    ];

    return (
        <>
            {sidebarOpen && (
                <div
                    style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.3)', zIndex: 99 }}
                    onClick={() => setSidebarOpen(false)}
                />
            )}
            <aside className={`sidebar ${sidebarOpen ? 'open' : ''}`}>
                <div className="sidebar-header">
                    <img src="assets/img/vocal-logotype-white.svg" alt="Vocal" className="logo" />
                    <span className="sidebar-title">Vocal Admin</span>
                </div>

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

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

// ==================== DASHBOARD ====================
const DashboardView = () => {
    const { user, notify, navigate } = useApp();
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);

    const load = async (silent = false) => {
        if (silent) setRefreshing(true); else setLoading(true);
        try {
            const r = await api.get('/api/admin/dashboard/stats');
            if (r?.error) throw new Error(r.error);
            setData(r);
        } catch (e) {
            notify.error('Erreur dashboard: ' + e.message);
        } finally {
            setLoading(false);
            setRefreshing(false);
        }
    };

    useEffect(() => { load(); /* eslint-disable-next-line */ }, []);
    // Auto refresh toutes les 60s
    useEffect(() => {
        const t = setInterval(() => load(true), 60000);
        return () => clearInterval(t);
    }, []);

    const fmtCHF = (n) => `${(Number(n) || 0).toLocaleString('fr-CH', { maximumFractionDigits: 2 })} CHF`;
    const fmtNum = (n) => (Number(n) || 0).toLocaleString('fr-CH');
    const fmtDur = (sec) => {
        const s = Number(sec) || 0;
        if (s < 60) return `${s} s`;
        if (s < 3600) return `${Math.round(s / 60)} min`;
        return `${(s / 3600).toFixed(1)} h`;
    };
    const fmtDay = (iso) => {
        try {
            const d = new Date(iso + 'T00:00:00');
            return d.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit' });
        } catch { return iso; }
    };

    if (loading) {
        return (
            <>
                <div className="page-header">
                    <div>
                        <h1 className="page-title">Dashboard</h1>
                        <p className="page-subtitle">Vue d'ensemble de la plateforme</p>
                    </div>
                </div>
                <div className="page-content">
                    <SkeletonStatsGrid count={4} />
                </div>
            </>
        );
    }

    const c = data?.calls || {};
    const r = data?.revenue || {};
    const cnt = data?.counters || {};
    const ts = c.timeseries_30d || [];
    const maxCalls = Math.max(1, ...ts.map(d => d.calls || 0));
    const deltaPos = (c.delta_pct_vs_yesterday || 0) >= 0;

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Dashboard</h1>
                    <p className="page-subtitle">Vue d'ensemble live de la plateforme · auto-refresh 60s</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-secondary btn-sm" onClick={() => load(true)} disabled={refreshing}>
                        <Icons.Refresh className="w-4 h-4" /> {refreshing ? 'Sync...' : 'Actualiser'}
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={() => navigate('analytics-platform')}>
                        <Icons.PieChart className="w-4 h-4" /> Analytics complète
                    </button>
                </div>
            </div>

            <div className="page-content">
                {/* ==================== APPELS — bloc principal ==================== */}
                <div style={{
                    display: 'grid',
                    gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',
                    gap: '0.75rem',
                    marginBottom: '0.5rem',
                }}>
                    <div className="card" style={{ padding: '1rem 1.1rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', marginBottom: '0.4rem' }}>
                            <div className="stat-icon orange" style={{ width: 36, height: 36, borderRadius: 10 }}>
                                <Icons.PhoneIncoming className="w-5 h-5" />
                            </div>
                            <div style={{ fontSize: '0.72rem', color: '#64748b', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.04em' }}>
                                Appels aujourd'hui
                            </div>
                        </div>
                        <div style={{ fontSize: '1.85rem', fontWeight: 800, color: '#0f172a', lineHeight: 1 }}>{fmtNum(c.today)}</div>
                        <div style={{ marginTop: '0.4rem', display: 'flex', alignItems: 'center', gap: '0.35rem', fontSize: '0.75rem' }}>
                            <span style={{ color: deltaPos ? '#16a34a' : '#dc2626', fontWeight: 700 }}>
                                {deltaPos ? '▲' : '▼'} {Math.abs(c.delta_pct_vs_yesterday || 0).toFixed(1)}%
                            </span>
                            <span style={{ color: '#94a3b8' }}>vs hier ({fmtNum(c.yesterday)})</span>
                        </div>
                        <div style={{ marginTop: '0.3rem', fontSize: '0.72rem', color: '#64748b' }}>
                            Durée totale : {fmtDur(c.today_duration_sec)}
                        </div>
                    </div>

                    <div className="card" style={{ padding: '1rem 1.1rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', marginBottom: '0.4rem' }}>
                            <div className="stat-icon blue" style={{ width: 36, height: 36, borderRadius: 10 }}>
                                <Icons.Calendar className="w-5 h-5" />
                            </div>
                            <div style={{ fontSize: '0.72rem', color: '#64748b', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.04em' }}>
                                Appels 7 jours
                            </div>
                        </div>
                        <div style={{ fontSize: '1.85rem', fontWeight: 800, color: '#0f172a', lineHeight: 1 }}>{fmtNum(c.last_7d)}</div>
                        <div style={{ marginTop: '0.4rem', fontSize: '0.72rem', color: '#64748b' }}>
                            ↗ Entrants : {fmtNum(c.by_direction_7d?.inbound)} · ↙ Sortants : {fmtNum(c.by_direction_7d?.outbound)}
                        </div>
                        <div style={{ marginTop: '0.3rem', fontSize: '0.72rem', color: '#64748b' }}>
                            Durée : {fmtDur(c.last_7d_duration_sec)}
                        </div>
                    </div>

                    <div className="card" style={{ padding: '1rem 1.1rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', marginBottom: '0.4rem' }}>
                            <div className="stat-icon purple" style={{ width: 36, height: 36, borderRadius: 10 }}>
                                <Icons.Activity className="w-5 h-5" />
                            </div>
                            <div style={{ fontSize: '0.72rem', color: '#64748b', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.04em' }}>
                                Appels 30 jours
                            </div>
                        </div>
                        <div style={{ fontSize: '1.85rem', fontWeight: 800, color: '#0f172a', lineHeight: 1 }}>{fmtNum(c.last_30d)}</div>
                        <div style={{ marginTop: '0.4rem', fontSize: '0.72rem', color: '#64748b' }}>
                            Mois en cours : {fmtNum(c.month)}
                        </div>
                        <div style={{ marginTop: '0.3rem', fontSize: '0.72rem', color: '#64748b' }}>
                            Durée : {fmtDur(c.last_30d_duration_sec)}
                        </div>
                    </div>

                    <div className="card" style={{ padding: '1rem 1.1rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', marginBottom: '0.4rem' }}>
                            <div className="stat-icon red" style={{ width: 36, height: 36, borderRadius: 10 }}>
                                <Icons.Phone className="w-5 h-5" />
                            </div>
                            <div style={{ fontSize: '0.72rem', color: '#64748b', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.04em' }}>
                                Appels en cours
                            </div>
                        </div>
                        <div style={{ fontSize: '1.85rem', fontWeight: 800, color: '#0f172a', lineHeight: 1 }}>{fmtNum(cnt.active_calls_now)}</div>
                        <div style={{ marginTop: '0.4rem', fontSize: '0.72rem', color: '#64748b' }}>
                            En direct sur la plateforme
                        </div>
                        <div style={{ marginTop: '0.3rem', fontSize: '0.72rem', color: cnt.unread_errors_24h > 0 ? '#dc2626' : '#94a3b8', fontWeight: cnt.unread_errors_24h > 0 ? 600 : 400 }}>
                            {cnt.unread_errors_24h > 0 ? `⚠️ ${cnt.unread_errors_24h} erreur(s) 24h` : 'Aucune erreur 24h'}
                        </div>
                    </div>
                </div>

                {/* ==================== Mini-graph appels 30 derniers jours ==================== */}
                <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem', flexWrap: 'wrap', gap: '0.5rem' }}>
                        <div>
                            <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a' }}>📞 Volume d'appels — 30 derniers jours</div>
                            <div style={{ fontSize: '0.75rem', color: '#64748b', marginTop: '0.15rem' }}>
                                Total : <strong>{fmtNum(c.last_30d)}</strong> appels · pic : <strong>{fmtNum(maxCalls)}</strong>/jour
                            </div>
                        </div>
                        <button className="btn btn-secondary btn-sm" onClick={() => navigate('calls')}>
                            Voir tous les appels →
                        </button>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'flex-end', gap: '3px', height: 140, padding: '0 2px' }}>
                        {ts.map((d, i) => {
                            const h = Math.max(2, (d.calls / maxCalls) * 100);
                            const isToday = i === ts.length - 1;
                            return (
                                <div key={d.day} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }} title={`${d.day} : ${d.calls} appels (${d.inbound} entrants, ${d.outbound} sortants)`}>
                                    <div style={{
                                        width: '100%',
                                        height: `${h}%`,
                                        background: isToday
                                            ? 'linear-gradient(180deg, #6366f1 0%, #4f46e5 100%)'
                                            : 'linear-gradient(180deg, #818cf8 0%, #6366f1 100%)',
                                        borderRadius: '4px 4px 2px 2px',
                                        transition: 'opacity .2s',
                                        opacity: d.calls === 0 ? 0.18 : 1,
                                    }} />
                                    <div style={{ fontSize: '0.6rem', color: '#94a3b8', fontWeight: isToday ? 700 : 400 }}>
                                        {(i % 5 === 0 || isToday) ? fmtDay(d.day) : ''}
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                </div>

                {/* ==================== Plateforme — KPIs business ==================== */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '0.75rem', marginBottom: '1rem' }}>
                    <StatCard label="Total clients actifs" value={fmtNum(cnt.total_clients)} color="#2563eb" icon={<Icons.Users className="w-5 h-5" />} />
                    <StatCard label="Lignes actives" value={fmtNum(cnt.total_lines)} color="#16a34a" icon={<Icons.Phone className="w-5 h-5" />} />
                    <StatCard label="MRR (lignes + modules)" value={fmtCHF(r.mrr)} color="#0284c7" icon={<Icons.TrendingUp className="w-5 h-5" />} />
                    <StatCard label="Abonnements actifs" value={fmtNum(r.active_subscriptions)} color="#0d9488" icon={<Icons.CreditCard className="w-5 h-5" />} />
                    <StatCard label={`Revenu mois (${r.month_purchases || 0} achats)`} value={fmtCHF(r.month)} color="#16a34a" icon={<Icons.DollarSign className="w-5 h-5" />} />
                    <StatCard label={`Revenu aujourd'hui (${r.today_purchases || 0})`} value={fmtCHF(r.today)} color="#a855f7" icon={<Icons.Gift className="w-5 h-5" />} />
                    <StatCard label="SMS envoyés (7j)" value={fmtNum(data?.messaging?.sms_7d)} color="#f59e0b" icon={<Icons.Send className="w-5 h-5" />} />
                    <StatCard label="WhatsApp (7j)" value={fmtNum(data?.messaging?.whatsapp_7d)} color="#10b981" icon={<Icons.MessageSquare className="w-5 h-5" />} />
                </div>

                {/* ==================== Top clients par volume d'appels (7j) ==================== */}
                {Array.isArray(data?.top_clients_calls_7d) && data.top_clients_calls_7d.length > 0 && (
                    <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            🏆 Top 5 clients par volume d'appels (7 jours)
                        </div>
                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                            {data.top_clients_calls_7d.map((tc, i) => (
                                <div key={tc.id} style={{
                                    display: 'flex', alignItems: 'center', gap: '0.75rem',
                                    padding: '0.6rem 0.85rem', background: '#f8fafc',
                                    borderRadius: 8, border: '1px solid var(--border)',
                                }}>
                                    <div style={{
                                        width: 26, height: 26, borderRadius: '50%',
                                        background: i === 0 ? '#fbbf24' : i === 1 ? '#cbd5e1' : i === 2 ? '#fed7aa' : '#e2e8f0',
                                        color: '#0f172a', display: 'flex', alignItems: 'center', justifyContent: 'center',
                                        fontWeight: 800, fontSize: '0.78rem', flexShrink: 0,
                                    }}>{i + 1}</div>
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{ fontWeight: 700, fontSize: '0.85rem', color: '#0f172a', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                            {tc.name || `Client #${tc.id}`}
                                        </div>
                                        <div style={{ fontSize: '0.72rem', color: '#64748b' }}>
                                            {fmtNum(tc.calls)} appels · {fmtDur(tc.duration)}
                                        </div>
                                    </div>
                                    <div style={{ fontSize: '0.95rem', fontWeight: 800, color: '#6366f1' }}>
                                        {fmtNum(tc.calls)}
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                )}

                <div className="card">
                    <div className="card-body" style={{ padding: '1.5rem' }}>
                        <h3 style={{ margin: '0 0 0.4rem', fontWeight: 700, fontSize: '1rem' }}>
                            Bienvenue, {user?.name || user?.email || 'Admin'}
                        </h3>
                        <p style={{ margin: 0, color: 'var(--text-muted)', fontSize: '0.82rem' }}>
                            Pour une analyse approfondie de la plateforme (timeseries, distribution horaire, statuts d'appels, conversations multi-canal,
                            voice bots, modules, erreurs système…), ouvrez le menu <strong>Analytics</strong>.
                        </p>
                    </div>
                </div>
            </div>
        </>
    );
};

// ==================== CLIENTS ====================
const ClientsView = () => {
    const { notify, setSelectedClient, navigate } = useApp();
    const [clients, setClients] = useState([]);
    const [loading, setLoading] = useState(true);
    const [search, setSearch] = useState('');
    const [showModal, setShowModal] = useState(false);
    const [editing, setEditing] = useState(null);
    const [saving, setSaving] = useState(false);
    const [form, setForm] = useState({ name: '', email: '', phone: '', company: '', notes: '' });
    const [creditTarget, setCreditTarget] = useState(null);
    const [creditForm, setCreditForm] = useState({ amount: '', description: '' });
    const [creditSaving, setCreditSaving] = useState(false);
    const [loginAsLoading, setLoginAsLoading] = useState(null);

    const loadClients = async () => {
        setLoading(true);
        try {
            const data = await api.get('/api/admin/clients');
            setClients(Array.isArray(data) ? data : data.clients || []);
        } catch (e) { console.error(e); }
        finally { setLoading(false); }
    };

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

    const openCreate = () => {
        setEditing(null);
        setForm({ name: '', email: '', phone: '', company: '', notes: '' });
        setShowModal(true);
    };

    const openEdit = (c) => {
        setEditing(c);
        setForm({ name: c.name || '', email: c.email || '', phone: c.phone || '', company: c.company || '', notes: c.notes || '' });
        setShowModal(true);
    };

    const handleSave = async () => {
        if (!form.name.trim()) return notify.error('Le nom est requis');
        setSaving(true);
        try {
            if (editing) {
                await api.put(`/api/admin/clients/${editing.id}`, form);
                notify.success('Client modifié');
            } else {
                await api.post('/api/admin/clients', form);
                notify.success('Client créé');
            }
            setShowModal(false);
            loadClients();
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setSaving(false); }
    };

    const handleToggle = async (c) => {
        try {
            await api.put(`/api/admin/clients/${c.id}`, { is_active: c.is_active ? 0 : 1 });
            notify.success(c.is_active ? 'Client désactivé' : 'Client activé');
            loadClients();
        } catch (e) { notify.error('Erreur'); }
    };

    const handleDelete = async (c) => {
        if (!confirm(`Supprimer le client "${c.name}" ? Cette action est irréversible.`)) return;
        try {
            await api.del(`/api/admin/clients/${c.id}`);
            notify.success('Client supprimé');
            loadClients();
        } catch (e) { notify.error('Erreur'); }
    };

    const openCredit = (c) => {
        setCreditTarget(c);
        setCreditForm({ amount: '', description: '' });
    };

    const handleCredit = async () => {
        const amount = parseFloat(creditForm.amount);
        if (!amount || amount <= 0) return notify.error('Montant invalide');
        setCreditSaving(true);
        try {
            const r = await api.post(`/api/admin/clients/${creditTarget.id}/credit`, {
                amount,
                description: creditForm.description || undefined,
            });
            if (r?.error) { notify.error(r.error); return; }
            const summary = `+${amount.toFixed(2)} ${r.currency || 'CHF'} crédités · solde: ${(r.new_balance ?? 0).toFixed(2)}`;
            if (r.email_sent) {
                notify.success(`${summary} · email de confirmation envoyé`);
            } else if (r.email_error) {
                notify.error(`${summary}, mais email non envoyé: ${r.email_error}`);
            } else if (creditTarget && !creditTarget.email) {
                notify.success(`${summary} (pas d'email - aucune notification envoyée)`);
            } else {
                notify.success(summary);
            }
            setCreditTarget(null);
            loadClients();
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setCreditSaving(false); }
    };

    const handleLoginAs = async (c, target = null) => {
        if (!c.email) return notify.error("Ce client n'a pas d'email");
        const label = target === 'wa' ? 'wa.helvia.app (WhatsApp)' : 'my.helvia.app';
        if (!confirm(`Ouvrir ${label} en tant que "${c.name}" (${c.email}) ?\n\nUne nouvelle session sera créée pour cet utilisateur.`)) return;
        setLoginAsLoading(`${c.id}:${target || 'my'}`);
        try {
            const r = await api.post(`/api/admin/clients/${c.id}/login-as`, target ? { target } : undefined);
            if (r?.error) { notify.error(r.error); return; }
            if (!r?.target_url) { notify.error("Réponse invalide de l'API"); return; }
            window.open(r.target_url, '_blank', 'noopener,noreferrer');
            notify.success(`Session ouverte pour ${c.email} sur ${label}`);
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setLoginAsLoading(null); }
    };

    const filtered = useMemo(() => {
        if (!search) return clients;
        const s = search.toLowerCase();
        return clients.filter(c =>
            (c.name || '').toLowerCase().includes(s) ||
            (c.email || '').toLowerCase().includes(s) ||
            (c.company || '').toLowerCase().includes(s)
        );
    }, [clients, search]);

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Clients</h1>
                    <p className="page-subtitle">{clients.length} clients enregistrés</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-secondary btn-sm" onClick={loadClients}>
                        <Icons.Refresh className="w-4 h-4" /> Actualiser
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={openCreate}>
                        <Icons.Plus className="w-4 h-4" /> Nouveau client
                    </button>
                </div>
            </div>
            <div className="page-content">
                <div style={{ marginBottom: '1rem', position: 'relative', maxWidth: '320px' }}>
                    <Icons.Search className="w-4 h-4" style={{ position: 'absolute', left: '0.75rem', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted)' }} />
                    <input type="text" value={search} onChange={(e) => setSearch(e.target.value)}
                        placeholder="Rechercher un client..." className="form-input" style={{ paddingLeft: '2.25rem' }} />
                </div>

                {loading ? (
                    <SkeletonTable rows={8} cols={7} />
                ) : filtered.length === 0 ? (
                    <div className="empty-state">
                        <Icons.Users className="empty-state-icon" />
                        <h3>Aucun client</h3>
                        <p>Les clients apparaîtront ici une fois créés.</p>
                    </div>
                ) : (
                    <div className="table-container">
                        <table className="data-table">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Nom</th>
                                    <th>Email</th>
                                    <th>Lignes</th>
                                    <th>Statut</th>
                                    <th>Créé le</th>
                                    <th>Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {filtered.map((c) => (
                                    <tr key={c.id || c.email} style={{ cursor: 'pointer' }} onClick={() => { setSelectedClient(c); navigate('client-detail'); }}>
                                        <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.78rem', color: 'var(--text-secondary)' }}>
                                          {c.client_code || <span style={{ color: 'var(--text-muted)' }}>—</span>}
                                        </td>
                                        <td style={{ fontWeight: 600 }}>{c.name || '-'}</td>
                                        <td>{c.email || '-'}</td>
                                        <td>{c.lines_count ?? 0}</td>
                                        <td>
                                            <span className={`badge ${c.is_active !== false && c.is_active !== 0 ? 'badge-success' : 'badge-gray'}`}
                                                  style={{ cursor: 'pointer' }} onClick={(e) => { e.stopPropagation(); handleToggle(c); }}>
                                                {c.is_active !== false && c.is_active !== 0 ? 'Actif' : 'Inactif'}
                                            </span>
                                        </td>
                                        <td>{formatDate(c.created_at)}</td>
                                        <td>
                                            <div style={{ display: 'flex', gap: '0.25rem' }}>
                                                <button className="btn btn-secondary btn-icon btn-sm" title="Offrir des crédits"
                                                    onClick={(e) => { e.stopPropagation(); openCredit(c); }}>
                                                    <Icons.Gift className="w-4 h-4" />
                                                </button>
                                                <button className="btn btn-secondary btn-icon btn-sm" title="Se connecter sur my.helvia.app"
                                                    disabled={loginAsLoading === `${c.id}:my` || !c.email}
                                                    onClick={(e) => { e.stopPropagation(); handleLoginAs(c); }}>
                                                    <Icons.LogIn className="w-4 h-4" />
                                                </button>
                                                <button className="btn btn-secondary btn-icon btn-sm" title="Se connecter sur wa.helvia.app (WhatsApp)"
                                                    disabled={loginAsLoading === `${c.id}:wa` || !c.email}
                                                    onClick={(e) => { e.stopPropagation(); handleLoginAs(c, 'wa'); }}
                                                    style={{ color: '#25D366' }}>
                                                    <Icons.MessageSquare className="w-4 h-4" />
                                                </button>
                                                <button className="btn btn-secondary btn-icon btn-sm" title="Modifier"
                                                    onClick={(e) => { e.stopPropagation(); openEdit(c); }}>
                                                    <Icons.Edit className="w-4 h-4" />
                                                </button>
                                                <button className="btn btn-danger btn-icon btn-sm" title="Supprimer"
                                                    onClick={(e) => { e.stopPropagation(); handleDelete(c); }}>
                                                    <Icons.Trash className="w-4 h-4" />
                                                </button>
                                            </div>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {creditTarget && (
                <div className="modal-overlay" onClick={() => setCreditTarget(null)}>
                    <div className="modal-content" onClick={(e) => e.stopPropagation()} style={{ maxWidth: '460px' }}>
                        <div className="modal-header">
                            <h2 className="modal-title" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                <Icons.Gift className="w-5 h-5" />
                                Offrir des crédits
                            </h2>
                            <button className="modal-close" onClick={() => setCreditTarget(null)}>&times;</button>
                        </div>
                        <div className="modal-body">
                            <p style={{ margin: '0 0 1rem', color: '#64748b', fontSize: '0.875rem' }}>
                                Pour <strong style={{ color: '#0f172a' }}>{creditTarget.name}</strong>
                                {creditTarget.email ? <> · {creditTarget.email}</> : null}
                                <br />
                                Solde actuel : <strong>{(creditTarget.balance ?? 0).toFixed(2)} {creditTarget.balance_currency || 'CHF'}</strong>
                            </p>
                            <div className="form-group">
                                <label className="form-label">Montant offert (CHF) *</label>
                                <input className="form-input" type="number" min="0.01" step="0.01" autoFocus
                                    value={creditForm.amount}
                                    onChange={(e) => setCreditForm({ ...creditForm, amount: e.target.value })}
                                    placeholder="ex: 50.00" />
                                <div style={{ display: 'flex', gap: '0.375rem', marginTop: '0.5rem', flexWrap: 'wrap' }}>
                                    {[10, 25, 50, 100, 250].map(v => (
                                        <button key={v} type="button" className="btn btn-secondary btn-sm"
                                            onClick={() => setCreditForm(f => ({ ...f, amount: String(v) }))}>
                                            +{v} CHF
                                        </button>
                                    ))}
                                </div>
                            </div>
                            <div className="form-group">
                                <label className="form-label">Description (optionnel)</label>
                                <input className="form-input" value={creditForm.description}
                                    onChange={(e) => setCreditForm({ ...creditForm, description: e.target.value })}
                                    placeholder="ex: Cadeau de bienvenue, compensation, ..." />
                                <small style={{ color: '#94a3b8', fontSize: '0.75rem' }}>
                                    Apparaîtra dans l'historique du wallet client et dans l'email de confirmation.
                                </small>
                            </div>
                            <div style={{
                                background: creditTarget.email ? '#ecfdf5' : '#fef3c7',
                                border: `1px solid ${creditTarget.email ? '#a7f3d0' : '#fde68a'}`,
                                borderRadius: 8,
                                padding: '0.625rem 0.75rem',
                                fontSize: '0.8rem',
                                color: creditTarget.email ? '#065f46' : '#92400e',
                                display: 'flex', alignItems: 'center', gap: '0.5rem'
                            }}>
                                <Icons.Mail className="w-4 h-4" />
                                {creditTarget.email
                                    ? <>Un email de confirmation sera envoyé à <strong>{creditTarget.email}</strong></>
                                    : <>Aucun email associé - le client ne recevra pas de notification.</>}
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setCreditTarget(null)} disabled={creditSaving}>Annuler</button>
                            <button className="btn btn-primary" onClick={handleCredit} disabled={creditSaving || !creditForm.amount}>
                                {creditSaving ? 'Crédit en cours…' : 'Créditer le wallet'}
                            </button>
                        </div>
                    </div>
                </div>
            )}

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: '500px' }}>
                        <div className="modal-header">
                            <h2 className="modal-title">{editing ? 'Modifier le client' : 'Nouveau client'}</h2>
                            <button className="modal-close" onClick={() => setShowModal(false)}>&times;</button>
                        </div>
                        <div className="modal-body">
                            <div className="form-group">
                                <label className="form-label">Nom *</label>
                                <input className="form-input" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="Nom du client" autoFocus />
                            </div>
                            <div className="form-group">
                                <label className="form-label">Email</label>
                                <input className="form-input" type="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} placeholder="email@exemple.com" />
                            </div>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                                <div className="form-group">
                                    <label className="form-label">Téléphone</label>
                                    <input className="form-input" value={form.phone} onChange={(e) => setForm({ ...form, phone: e.target.value })} placeholder="+41..." />
                                </div>
                                <div className="form-group">
                                    <label className="form-label">Entreprise</label>
                                    <input className="form-input" value={form.company} onChange={(e) => setForm({ ...form, company: e.target.value })} placeholder="Nom de l'entreprise" />
                                </div>
                            </div>
                            <div className="form-group">
                                <label className="form-label">Notes</label>
                                <textarea className="form-input" value={form.notes} onChange={(e) => setForm({ ...form, notes: e.target.value })} placeholder="Notes internes..." rows={3} style={{ resize: 'vertical' }} />
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={handleSave} disabled={saving}>
                                {saving ? 'Enregistrement...' : editing ? 'Enregistrer' : 'Créer le client'}
                            </button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== CLIENT DETAIL VIEW ====================
const ClientDetailView = () => {
    const { selectedClient, navigate, notify } = useApp();
    const [tab, setTab] = useState('summary');
    const [clientLines, setClientLines] = useState([]);
    const [allLines, setAllLines] = useState([]);
    const [widgets, setWidgets] = useState([]);
    const [loadingLines, setLoadingLines] = useState(false);
    const [calls, setCalls] = useState([]);
    const [loadingCalls, setLoadingCalls] = useState(false);
    const [showCreateWidget, setShowCreateWidget] = useState(false);
    const [newWidgetLabel, setNewWidgetLabel] = useState('');
    const [newWidgetLineIds, setNewWidgetLineIds] = useState([]);
    const [creatingWidget, setCreatingWidget] = useState(false);
    const [copied, setCopied] = useState(null);
    const [twilioAccounts, setTwilioAccounts] = useState([]);
    const [allTwilioAccounts, setAllTwilioAccounts] = useState([]);
    const [transferring, setTransferring] = useState(null);
    const [bundleCopiesByTwAcc, setBundleCopiesByTwAcc] = useState({}); // { twAccId: { copies, loading } }
    const [creatingBundleCopy, setCreatingBundleCopy] = useState(null);
    const [transferTargetByLine, setTransferTargetByLine] = useState({});
    const [twLocateByLine, setTwLocateByLine] = useState({}); // { lineId: { actual_account_id, actual_account_sid, actual_account_label, in_sync, loading } }
    const [infoModal, setInfoModal] = useState(null); // { title, text }
    const [creatingSubaccount, setCreatingSubaccount] = useState(false);
    const [orders, setOrders] = useState(null);
    const [loadingOrders, setLoadingOrders] = useState(false);

    const loadOrders = useCallback(async () => {
        if (!selectedClient?.id) return;
        setLoadingOrders(true);
        try {
            const data = await api.get(`/api/admin/clients/${selectedClient.id}/orders`);
            if (!data?.error) setOrders(data);
        } catch (e) { console.error('[orders]', e); }
        finally { setLoadingOrders(false); }
    }, [selectedClient?.id]);

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

    const loadData = useCallback(async () => {
        if (!selectedClient?.id) return;
        setLoadingLines(true);
        try {
            const [linesData, allLinesData, keysData, twAccData] = await Promise.all([
                api.get(`/api/lines`),
                api.get(`/api/lines`),
                api.get(`/api/api-keys?client_id=${selectedClient.id}`),
                api.get(`/api/twilio-accounts`).catch(() => ({ accounts: [] })),
            ]);
            const lines = linesData.lines || [];
            const cl = lines.filter(l => l.client_id === selectedClient.id);
            setClientLines(cl);
            setAllLines(lines);
            setWidgets(Array.isArray(keysData) ? keysData : []);
            const allTw = twAccData.accounts || twAccData || [];
            setTwilioAccounts(allTw.filter(t => t.client_id === selectedClient.id));
            setAllTwilioAccounts(allTw);
        } catch (e) { console.error(e); }
        finally { setLoadingLines(false); }
    }, [selectedClient?.id]);

    const transferLineToAccount = async (line, targetAccountId) => {
        if (!targetAccountId) return;
        const target = allTwilioAccounts.find(a => String(a.id) === String(targetAccountId));
        if (!target) return;
        if (!confirm(`Transférer le numéro ${line.phone_number} vers le compte Twilio :\n\n${target.name || target.account_sid}\n\nCette opération déplace le numéro chez Twilio (IncomingPhoneNumber.AccountSid). Continuer ?`)) return;
        setTransferring(line.id);
        try {
            const r = await api.post(`/api/lines/${line.id}/transfer-twilio-account`, { twilio_account_id: parseInt(targetAccountId) });
            if (r?.error) {
                setInfoModal({ title: 'Erreur transfert', text: r.error + (r.detail ? `\n\n${JSON.stringify(r.detail, null, 2)}` : '') });
            } else {
                const tr = r.transfer || {};
                let msg;
                if (tr.transferred) {
                    const bundlePicked = tr.clone_info?.bundle_picked_on_target;
                    const bundleLine = bundlePicked
                        ? `Bundle (target) : ${bundlePicked}`
                        : `Bundle (target) : aucun trouvé sur le compte cible`;
                    msg = `Transfert REEL effectue chez Twilio.\n\nDe : ${tr.from_account_sid}\nVers : ${tr.to_account_sid}\nPN-SID : ${tr.pn_sid}\n${bundleLine}`;
                } else if (tr.skipped === 'already_on_target') {
                    msg = `Aucun transfert necessaire : la DB pointait deja sur le compte cible.`;
                } else if (tr.skipped === 'same_account_sid') {
                    msg = `Pas de transfert Twilio : le numero est DEJA sur ce compte chez Twilio (la DB a juste ete synchronisee).\nPN-SID : ${tr.pn_sid}\nReal owner SID : ${tr.real_owner_sid || '-'}`;
                } else {
                    msg = '';
                }
                setInfoModal({ title: 'Resultat transfert', text: `${msg}\n\n---\nJSON brut :\n${JSON.stringify(r, null, 2)}` });
                setTransferTargetByLine(s => ({ ...s, [line.id]: '' }));
                await loadData();
                await locateLineTwilio(line.id);
            }
        } catch (e) { alert('Erreur : ' + e.message); }
        finally { setTransferring(null); }
    };

    const InfoModalView = () => {
        if (!infoModal) return null;
        const ref = useRef(null);
        return (
            <div onClick={() => setInfoModal(null)}
                style={{ position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.6)', backdropFilter: 'blur(4px)', zIndex: 9000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1.5rem' }}>
                <div onClick={e => e.stopPropagation()}
                    style={{ background: '#fff', borderRadius: 14, maxWidth: 900, width: '100%', maxHeight: '90vh', display: 'flex', flexDirection: 'column', boxShadow: '0 25px 60px rgba(0,0,0,0.3)' }}>
                    <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e5e7eb', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                        <h3 style={{ margin: 0, fontSize: '1rem' }}>{infoModal.title}</h3>
                        <div style={{ display: 'flex', gap: '0.5rem' }}>
                            <button className="btn btn-secondary btn-sm" onClick={() => {
                                navigator.clipboard.writeText(infoModal.text).then(() => {});
                            }}>📋 Copier</button>
                            <button className="btn btn-secondary btn-sm" onClick={() => {
                                if (!ref.current) return;
                                ref.current.focus(); ref.current.select();
                            }}>Sélectionner</button>
                            <button className="btn btn-secondary btn-sm" onClick={() => setInfoModal(null)}>×</button>
                        </div>
                    </div>
                    <textarea ref={ref} readOnly value={infoModal.text}
                        style={{ flex: 1, padding: '1rem', fontFamily: 'ui-monospace, monospace', fontSize: '0.78rem', lineHeight: 1.4, border: 'none', outline: 'none', resize: 'none', whiteSpace: 'pre', minHeight: 400 }} />
                </div>
            </div>
        );
    };

    const loadBundleCopies = useCallback(async (twAccId) => {
        setBundleCopiesByTwAcc(s => ({ ...s, [twAccId]: { ...(s[twAccId] || {}), loading: true } }));
        try {
            const r = await api.get(`/api/twilio-accounts/${twAccId}/bundle-copies`);
            if (r?.error) setBundleCopiesByTwAcc(s => ({ ...s, [twAccId]: { error: r.error, loading: false } }));
            else setBundleCopiesByTwAcc(s => ({ ...s, [twAccId]: { copies: r.copies || [], loading: false } }));
        } catch (e) {
            setBundleCopiesByTwAcc(s => ({ ...s, [twAccId]: { error: e.message, loading: false } }));
        }
    }, []);

    useEffect(() => {
        twilioAccounts.forEach(t => { if (!bundleCopiesByTwAcc[t.id]) loadBundleCopies(t.id); });
    }, [twilioAccounts, loadBundleCopies]);

    const createBundleCopy = async (twAccId, country = 'CH') => {
        const label = country === 'FR' ? 'France 🇫🇷' : 'Suisse 🇨🇭';
        if (!confirm(`Cloner le Regulatory Bundle ${label} vers ce compte ?\n\nLe clone hérite du statut twilio-approved du master et est immédiatement utilisable pour acheter des numéros ${country}.`)) return;
        setCreatingBundleCopy(`${twAccId}:${country}`);
        try {
            const r = await api.post(`/api/twilio-accounts/${twAccId}/bundle-copies`, { country });
            if (r?.error) setInfoModal({ title: `Erreur clone bundle ${country}`, text: r.error + (r.detail ? '\n\n' + JSON.stringify(r.detail, null, 2) : '') });
            else { setInfoModal({ title: `Bundle ${label} cloné`, text: `Clone créé sur le subaccount.\n\nSID copie : ${r.copy_bundle_sid}\nStatut : ${r.status}\n\nLe clone est généralement twilio-approved immédiatement (héritage du master).` }); await loadBundleCopies(twAccId); }
        } catch (e) { setInfoModal({ title: 'Erreur', text: e.message }); }
        finally { setCreatingBundleCopy(null); }
    };

    const BUNDLE_STATUS_META = {
        'draft':            { label: 'Brouillon',   cls: 'badge-gray' },
        'pending-review':   { label: 'En attente review', cls: 'badge-warning' },
        'in-review':        { label: 'En review',   cls: 'badge-warning' },
        'twilio-approved':  { label: '✓ Approuvé',  cls: 'badge-success' },
        'twilio-rejected':  { label: '✗ Rejeté',    cls: 'badge-danger' },
        'provisionally-approved': { label: 'Approuvé (provisoire)', cls: 'badge-success' },
    };

    const locateLineTwilio = useCallback(async (lineId) => {
        setTwLocateByLine(s => ({ ...s, [lineId]: { ...(s[lineId] || {}), loading: true } }));
        try {
            const r = await api.get(`/api/lines/${lineId}/twilio-locate`);
            setTwLocateByLine(s => ({ ...s, [lineId]: { ...r, loading: false } }));
        } catch (e) {
            setTwLocateByLine(s => ({ ...s, [lineId]: { error: e.message, loading: false } }));
        }
    }, []);

    useEffect(() => {
        if (!clientLines.length) return;
        clientLines.forEach(l => { if (!twLocateByLine[l.id]) locateLineTwilio(l.id); });
    }, [clientLines, locateLineTwilio]);

    const reconcileLineTwilio = async (line) => {
        if (!confirm(`Réconcilier la DB pour la ligne ${line.phone_number} ?\n\nLa DB sera mise à jour pour pointer sur le compte Twilio où le numéro est réellement présent. Aucune action sur Twilio.`)) return;
        try {
            const r = await api.post(`/api/lines/${line.id}/twilio-reconcile`, {});
            if (r?.error) alert('Erreur : ' + r.error);
            else {
                alert('DB réconciliée ✔');
                await loadData();
                await locateLineTwilio(line.id);
            }
        } catch (e) { alert('Erreur : ' + e.message); }
    };

    const createSubaccount = async () => {
        if (!selectedClient?.id) return;
        if (!confirm(`Créer un subaccount Twilio pour "${selectedClient.name}" ? Cette action appelle Twilio et ne peut pas être annulée.`)) return;
        setCreatingSubaccount(true);
        try {
            const res = await api.post(`/api/admin/clients/${selectedClient.id}/twilio-subaccount`, {});
            if (res?.success && res.account) {
                notify.success(`Subaccount créé : ${res.account.account_sid}`);
                loadData();
            } else if (res?.existing) {
                notify.error(`Un subaccount existe déjà : ${res.existing.account_sid}`);
            } else {
                notify.error(res?.twilio_message || res?.error || 'Erreur lors de la création');
            }
        } catch (e) {
            notify.error('Erreur: ' + (e.message || 'inconnue'));
        } finally {
            setCreatingSubaccount(false);
        }
    };

    const loadCalls = useCallback(async () => {
        if (!selectedClient?.id) return;
        setLoadingCalls(true);
        try {
            const data = await api.get(`/api/calls?limit=20`);
            const allCalls = data.calls || [];
            const lineIds = clientLines.map(l => l.id);
            setCalls(allCalls.filter(c => lineIds.includes(c.line_id)));
        } catch (e) {}
        finally { setLoadingCalls(false); }
    }, [selectedClient?.id, clientLines]);

    useEffect(() => { loadData(); }, [loadData]);
    useEffect(() => { if (clientLines.length > 0) loadCalls(); }, [clientLines, loadCalls]);

    const createWidget = async () => {
        if (!newWidgetLabel.trim()) return notify.error('Nom requis');
        if (newWidgetLineIds.length === 0) return notify.error('Sélectionnez au moins une ligne');
        setCreatingWidget(true);
        try {
            const res = await api.post('/api/api-keys', {
                label: newWidgetLabel,
                client_id: selectedClient.id,
                line_ids: newWidgetLineIds,
            });
            if (res?.key_value) {
                notify.success('Widget créé');
                setShowCreateWidget(false);
                setNewWidgetLabel('');
                setNewWidgetLineIds([]);
                loadData();
            } else {
                notify.error(res?.error || 'Erreur');
            }
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setCreatingWidget(false); }
    };

    const updateWidgetLines = async (widgetId, lineIds) => {
        try {
            await api.put(`/api/api-keys/${widgetId}`, { line_ids: lineIds });
            notify.success('Lignes mises à jour');
            loadData();
        } catch (e) { notify.error('Erreur'); }
    };

    const deleteWidget = async (w) => {
        if (!confirm(`Supprimer le widget "${w.label}" ?`)) return;
        try {
            await api.del(`/api/api-keys/${w.id}`);
            notify.success('Widget supprimé');
            loadData();
        } catch (e) { notify.error('Erreur'); }
    };

    const toggleWidgetActive = async (w) => {
        try {
            await api.put(`/api/api-keys/${w.id}`, { is_active: w.is_active ? 0 : 1 });
            notify.success(w.is_active ? 'Widget désactivé' : 'Widget activé');
            loadData();
        } catch (e) { notify.error('Erreur'); }
    };

    const copyToClipboard = (text, id) => {
        navigator.clipboard.writeText(text);
        setCopied(id);
        setTimeout(() => setCopied(null), 2000);
    };

    const getWidgetSnippet = (w) => `<!-- Vocal Widget - ${w.label} -->
<script src="https://widget.helvia.app/assets/js/vocal-widget.js"><\/script>
<script>
  VocalWidget.init({
    token: '${w.key_value}',
  });
<\/script>`;

    const toggleLineForWidget = (lineId) => {
        setNewWidgetLineIds(prev =>
            prev.includes(lineId) ? prev.filter(id => id !== lineId) : [...prev, lineId]
        );
    };

    if (!selectedClient) return <div className="page-content"><p>Aucun client sélectionné.</p></div>;
    const c = selectedClient;

    return (
        <>
            <div className="page-header">
                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
                    <button className="btn btn-secondary btn-icon btn-sm" onClick={() => navigate('clients')}>
                        <Icons.ChevronLeft className="w-4 h-4" />
                    </button>
                    <div>
                        <h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', flexWrap: 'wrap' }}>
                            {c.name}
                            {c.client_code && (
                                <span
                                    title="ID client (cliquer pour copier)"
                                    onClick={() => { navigator.clipboard.writeText(c.client_code); notify.success('ID copié'); }}
                                    style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.7rem', fontWeight: 600, padding: '0.2rem 0.5rem', background: 'var(--bg-secondary)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text-secondary)', cursor: 'pointer', letterSpacing: '0.04em' }}
                                >
                                    {c.client_code}
                                </span>
                            )}
                        </h1>
                        <p className="page-subtitle">{c.company || c.email || 'Client'}</p>
                    </div>
                </div>
            </div>
            <div className="page-content">
                <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1.5rem', borderBottom: '1px solid var(--border)', paddingBottom: '0.5rem' }}>
                    {[{ id: 'summary', label: 'Résumé' }, { id: 'widget', label: 'Widget' }].map(t => (
                        <button key={t.id} onClick={() => setTab(t.id)}
                            style={{ padding: '0.5rem 1rem', fontSize: '0.85rem', fontWeight: tab === t.id ? 700 : 500, color: tab === t.id ? 'var(--primary)' : 'var(--text-muted)', background: 'none', border: 'none', borderBottom: tab === t.id ? '2px solid var(--primary)' : '2px solid transparent', cursor: 'pointer', marginBottom: '-0.55rem' }}>
                            {t.label}
                        </button>
                    ))}
                </div>

                {tab === 'summary' && (
                    <>
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700, margin: 0 }}>Informations</h3>
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                    <span style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Niveau client</span>
                                    <select
                                        value={c.level ?? 1}
                                        onChange={async e => {
                                            const lvl = parseInt(e.target.value);
                                            try {
                                                const r = await api.put(`/api/admin/clients/${c.id}`, { level: lvl });
                                                if (r?.error) alert('Erreur : ' + r.error);
                                                else { setSelectedClient({ ...c, level: lvl }); }
                                            } catch (err) { alert('Erreur : ' + err.message); }
                                        }}
                                        style={{ fontSize: '0.85rem', padding: '0.25rem 0.5rem', borderRadius: 6, border: '1px solid var(--border)', fontWeight: 600 }}
                                        title="Niveau de fonctionnalités exposées sur my.helvia.app (1 = minimal, 5 = tout)"
                                    >
                                        {[1, 2, 3, 4, 5].map(n => <option key={n} value={n}>Niveau {n}</option>)}
                                    </select>
                                </div>
                            </div>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                                <div>
                                  <span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>ID client</span>
                                  <p style={{ margin: '0.25rem 0 0', fontFamily: 'ui-monospace, monospace', fontWeight: 700 }}>
                                    {c.client_code || <span style={{ color: 'var(--text-muted)', fontWeight: 400 }}>— (sera généré au prochain backfill)</span>}
                                  </p>
                                </div>
                                <div><span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Nom</span><p style={{ margin: '0.25rem 0 0', fontWeight: 600 }}>{c.name}</p></div>
                                <div><span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Email</span><p style={{ margin: '0.25rem 0 0' }}>{c.email || '-'}</p></div>
                                <div><span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Téléphone</span><p style={{ margin: '0.25rem 0 0' }}>{c.phone || '-'}</p></div>
                                <div><span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Entreprise</span><p style={{ margin: '0.25rem 0 0' }}>{c.company || '-'}</p></div>
                                <div>
                                    <span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Solde actuel</span>
                                    <p style={{
                                        margin: '0.25rem 0 0',
                                        fontWeight: 700,
                                        fontSize: '1.05rem',
                                        color: (orders?.balance ?? c.balance ?? 0) < 5 ? '#dc2626'
                                             : (orders?.balance ?? c.balance ?? 0) < 20 ? '#d97706'
                                             : '#059669',
                                        fontFamily: 'ui-monospace, monospace',
                                    }}>
                                        {(orders?.balance ?? c.balance ?? 0).toFixed(2)} {orders?.currency || c.balance_currency || 'CHF'}
                                    </p>
                                </div>
                            </div>
                            {c.notes && <div style={{ marginTop: '1rem' }}><span style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase' }}>Notes</span><p style={{ margin: '0.25rem 0 0', fontSize: '0.85rem', color: 'var(--text-secondary)' }}>{c.notes}</p></div>}

                            {(c.signup_country || c.signup_ip || c.signup_user_agent || c.signup_referrer || c.signup_source || c.signup_landing_url) && (
                                <div style={{ marginTop: '1.25rem', paddingTop: '1rem', borderTop: '1px dashed var(--border)' }}>
                                    <div style={{ fontSize: '0.75rem', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', marginBottom: '0.6rem' }}>Origine de l'inscription</div>
                                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem', fontSize: '0.85rem' }}>
                                        <div><strong>Pays :</strong> {c.signup_country || '—'} {c.signup_city ? `· ${c.signup_city}` : ''}</div>
                                        <div><strong>IP :</strong> <span style={{ fontFamily: 'ui-monospace, monospace' }}>{c.signup_ip || '—'}</span></div>
                                        <div style={{ gridColumn: '1 / -1' }}><strong>Navigateur :</strong> <span style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>{parseUserAgent(c.signup_user_agent)}</span></div>
                                        <div><strong>Source :</strong> {c.signup_source || '—'}</div>
                                        <div><strong>Referrer :</strong> {c.signup_referrer ? <a href={c.signup_referrer} target="_blank" rel="noopener" style={{ wordBreak: 'break-all' }}>{c.signup_referrer}</a> : '—'}</div>
                                        {c.signup_landing_url && <div style={{ gridColumn: '1 / -1' }}><strong>Landing :</strong> <a href={c.signup_landing_url} target="_blank" rel="noopener" style={{ wordBreak: 'break-all' }}>{c.signup_landing_url}</a></div>}
                                    </div>
                                </div>
                            )}
                        </div>

                        {/* ===== Commandes & Cadeaux ===== */}
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700, margin: 0 }}>
                                    Commandes & Cadeaux {orders ? <span style={{ fontWeight: 400, color: 'var(--text-muted)' }}>({orders.counts?.total || 0})</span> : null}
                                </h3>
                                <button className="btn btn-secondary btn-sm" onClick={loadOrders} disabled={loadingOrders} title="Rafraîchir">
                                    {loadingOrders ? '…' : '↻'}
                                </button>
                            </div>

                            {orders && (
                                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '0.75rem', marginBottom: '1rem' }}>
                                    <div style={{ padding: '0.75rem', background: '#ecfdf5', borderRadius: 8, border: '1px solid #a7f3d0' }}>
                                        <div style={{ fontSize: '0.7rem', fontWeight: 700, color: '#065f46', textTransform: 'uppercase' }}>Solde actuel</div>
                                        <div style={{ fontWeight: 700, fontSize: '1.1rem', color: '#065f46', fontFamily: 'ui-monospace, monospace' }}>
                                            {(orders.balance || 0).toFixed(2)} {orders.currency}
                                        </div>
                                    </div>
                                    <div style={{ padding: '0.75rem', background: '#eff6ff', borderRadius: 8, border: '1px solid #bfdbfe' }}>
                                        <div style={{ fontSize: '0.7rem', fontWeight: 700, color: '#1e40af', textTransform: 'uppercase' }}>Total rechargé</div>
                                        <div style={{ fontWeight: 700, fontSize: '1.1rem', color: '#1e40af', fontFamily: 'ui-monospace, monospace' }}>
                                            {(orders.total_recharged || 0).toFixed(2)} {orders.currency}
                                        </div>
                                        <div style={{ fontSize: '0.7rem', color: '#1e40af', marginTop: 2 }}>{orders.counts?.recharges || 0} recharge{(orders.counts?.recharges || 0) > 1 ? 's' : ''}</div>
                                    </div>
                                    <div style={{ padding: '0.75rem', background: '#fdf4ff', borderRadius: 8, border: '1px solid #f5d0fe' }}>
                                        <div style={{ fontSize: '0.7rem', fontWeight: 700, color: '#86198f', textTransform: 'uppercase' }}>Cadeaux offerts</div>
                                        <div style={{ fontWeight: 700, fontSize: '1.1rem', color: '#86198f', fontFamily: 'ui-monospace, monospace' }}>
                                            {(orders.total_gifted || 0).toFixed(2)} {orders.currency}
                                        </div>
                                        <div style={{ fontSize: '0.7rem', color: '#86198f', marginTop: 2 }}>{orders.counts?.gifts || 0} cadeau{(orders.counts?.gifts || 0) > 1 ? 'x' : ''}</div>
                                    </div>
                                    <div style={{ padding: '0.75rem', background: '#fef2f2', borderRadius: 8, border: '1px solid #fecaca' }}>
                                        <div style={{ fontSize: '0.7rem', fontWeight: 700, color: '#991b1b', textTransform: 'uppercase' }}>Consommé</div>
                                        <div style={{ fontWeight: 700, fontSize: '1.1rem', color: '#991b1b', fontFamily: 'ui-monospace, monospace' }}>
                                            {(orders.total_consumed || 0).toFixed(2)} {orders.currency}
                                        </div>
                                    </div>
                                </div>
                            )}

                            {loadingOrders && !orders && <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', margin: 0 }}>Chargement…</p>}
                            {orders && orders.transactions.length === 0 && (
                                <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', margin: 0 }}>Aucune commande ni cadeau pour ce client.</p>
                            )}
                            {orders && orders.transactions.length > 0 && (
                                <div style={{ overflowX: 'auto', border: '1px solid var(--border)', borderRadius: 8 }}>
                                    <table style={{ width: '100%', fontSize: '0.82rem', borderCollapse: 'collapse' }}>
                                        <thead style={{ background: 'var(--bg-secondary)' }}>
                                            <tr style={{ textAlign: 'left' }}>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700 }}>Date</th>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700 }}>Type</th>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700 }}>Description</th>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700, textAlign: 'right' }}>Montant</th>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700, textAlign: 'right' }}>Solde après</th>
                                                <th style={{ padding: '0.5rem 0.75rem', fontWeight: 700 }}>Référence</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            {orders.transactions.map(t => {
                                                const amt = Number(t.amount) || 0;
                                                const isCredit = amt >= 0;
                                                const typeLabel = {
                                                    recharge:     { label: 'Recharge',     cls: 'badge-success', icon: '💳' },
                                                    bonus:        { label: 'Cadeau',       cls: 'badge-info',    icon: '🎁' },
                                                    pack:         { label: 'Pack',         cls: 'badge-warning', icon: '📦' },
                                                    subscription: { label: 'Abonnement',   cls: 'badge-info',    icon: '🔄' },
                                                    module:       { label: 'Module',       cls: 'badge-warning', icon: '🧩' },
                                                    refund:       { label: 'Remboursement', cls: 'badge-gray',    icon: '↩️' },
                                                    adjustment:   { label: 'Ajustement',   cls: 'badge-gray',    icon: '⚖️' },
                                                }[t.type] || { label: t.type, cls: 'badge-gray', icon: '•' };
                                                return (
                                                    <tr key={t.id} style={{ borderTop: '1px solid var(--border)' }}>
                                                        <td style={{ padding: '0.5rem 0.75rem', whiteSpace: 'nowrap', color: 'var(--text-secondary)' }}>
                                                            {new Date(t.created_at).toLocaleString('fr-CH', { dateStyle: 'short', timeStyle: 'short' })}
                                                        </td>
                                                        <td style={{ padding: '0.5rem 0.75rem', whiteSpace: 'nowrap' }}>
                                                            <span style={{ fontSize: '0.95rem', marginRight: 4 }}>{typeLabel.icon}</span>
                                                            <span className={`badge ${typeLabel.cls}`}>{typeLabel.label}</span>
                                                        </td>
                                                        <td style={{ padding: '0.5rem 0.75rem', maxWidth: 360 }}>
                                                            <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{t.description || '—'}</div>
                                                        </td>
                                                        <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontFamily: 'ui-monospace, monospace', fontWeight: 700, color: isCredit ? '#059669' : '#dc2626', whiteSpace: 'nowrap' }}>
                                                            {isCredit ? '+' : ''}{amt.toFixed(2)} {t.currency || orders.currency}
                                                        </td>
                                                        <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontFamily: 'ui-monospace, monospace', color: 'var(--text-secondary)', whiteSpace: 'nowrap' }}>
                                                            {t.balance_after != null ? `${Number(t.balance_after).toFixed(2)}` : '—'}
                                                        </td>
                                                        <td style={{ padding: '0.5rem 0.75rem', fontSize: '0.72rem', color: 'var(--text-muted)', fontFamily: 'ui-monospace, monospace' }}>
                                                            {t.stripe_payment_intent ? (
                                                                <a href={`https://dashboard.stripe.com/payments/${t.stripe_payment_intent}`} target="_blank" rel="noopener" style={{ color: '#635bff' }}>
                                                                    Stripe ↗
                                                                </a>
                                                            ) : t.reference_type ? `${t.reference_type}${t.reference_id ? '#' + t.reference_id : ''}` : '—'}
                                                        </td>
                                                    </tr>
                                                );
                                            })}
                                        </tbody>
                                    </table>
                                </div>
                            )}
                        </div>

                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.75rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700, margin: 0 }}>Subaccount Twilio</h3>
                                {twilioAccounts.length === 0 ? (
                                    <button className="btn btn-primary btn-sm" onClick={createSubaccount} disabled={creatingSubaccount}>
                                        <Icons.Plus className="w-4 h-4" /> {creatingSubaccount ? 'Création…' : 'Créer un subaccount'}
                                    </button>
                                ) : null}
                            </div>
                            {twilioAccounts.length === 0 ? (
                                <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', margin: 0 }}>
                                    Aucun subaccount Twilio dédié à ce client. Créez-en un pour isoler ses numéros, sa facturation et ses logs Twilio.
                                </p>
                            ) : (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                    {twilioAccounts.map(t => {
                                        const bcState = bundleCopiesByTwAcc[t.id] || {};
                                        const copies = bcState.copies || [];
                                        return (
                                            <div key={t.id} style={{ padding: '0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px solid var(--border)' }}>
                                                <div style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', alignItems: 'center', gap: '0.75rem' }}>
                                                    <div>
                                                        <div style={{ fontWeight: 600, fontSize: '0.85rem' }}>{t.name}</div>
                                                        <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)', fontFamily: 'monospace' }}>{t.account_sid}</div>
                                                    </div>
                                                    <span className={`badge ${t.is_active ? 'badge-success' : 'badge-gray'}`}>{t.is_active ? 'Actif' : 'Inactif'}</span>
                                                    <button className="btn btn-secondary btn-icon btn-sm" title="Copier le SID" onClick={() => { navigator.clipboard.writeText(t.account_sid); notify.success('SID copié'); }}>
                                                        <Icons.Copy className="w-4 h-4" />
                                                    </button>
                                                </div>
                                                <div style={{ marginTop: '0.75rem', paddingTop: '0.6rem', borderTop: '1px dashed var(--border)' }}>
                                                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }}>
                                                        <div style={{ fontSize: '0.78rem', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase' }}>
                                                            Regulatory Bundles ({copies.length})
                                                        </div>
                                                        <div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
                                                            <button className="btn btn-secondary btn-sm" disabled={bcState.loading} onClick={() => loadBundleCopies(t.id)} title="Rafraîchir la liste">↻</button>
                                                            <button className="btn btn-primary btn-sm"
                                                                disabled={creatingBundleCopy === `${t.id}:CH`}
                                                                onClick={() => createBundleCopy(t.id, 'CH')}
                                                                title="Cloner le Regulatory Bundle Suisse vers ce subaccount">
                                                                {creatingBundleCopy === `${t.id}:CH` ? 'Clonage…' : '🇨🇭 Clone CH bundle'}
                                                            </button>
                                                            <button className="btn btn-primary btn-sm"
                                                                disabled={creatingBundleCopy === `${t.id}:FR`}
                                                                onClick={() => createBundleCopy(t.id, 'FR')}
                                                                title="Cloner le Regulatory Bundle France vers ce subaccount (requis pour acheter des numéros +33)">
                                                                {creatingBundleCopy === `${t.id}:FR` ? 'Clonage…' : '🇫🇷 Clone & Activate FR bundle'}
                                                            </button>
                                                        </div>
                                                    </div>
                                                    {bcState.loading && <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Chargement…</div>}
                                                    {bcState.error && <div style={{ fontSize: '0.75rem', color: 'var(--danger, #c00)' }}>{bcState.error}</div>}
                                                    {!bcState.loading && !bcState.error && copies.length === 0 && (
                                                        <div style={{ fontSize: '0.78rem', color: 'var(--text-muted)' }}>
                                                            Aucun bundle cloné. Cliquez "+ Cloner bundle" pour démarrer.
                                                        </div>
                                                    )}
                                                    {copies.length > 0 && (
                                                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.3rem' }}>
                                                            {copies.map(bc => {
                                                                const meta = BUNDLE_STATUS_META[bc.status] || { label: bc.status, cls: 'badge-gray' };
                                                                return (
                                                                    <div key={bc.id} style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', alignItems: 'center', gap: '0.5rem', padding: '0.4rem 0.6rem', background: 'var(--bg-primary)', borderRadius: 6, border: '1px solid var(--border)' }}>
                                                                        <div>
                                                                            <div style={{ fontSize: '0.78rem', fontWeight: 600 }}>{bc.friendly_name || '(sans nom)'}</div>
                                                                            <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontFamily: 'ui-monospace, monospace' }}>
                                                                                {bc.copy_bundle_sid} · src {bc.source_bundle_sid.slice(0, 8)}…
                                                                            </div>
                                                                            {bc.last_polled_at && <div style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>Dernière vérif : {formatDateTime(bc.last_polled_at)}</div>}
                                                                        </div>
                                                                        <span className={`badge ${meta.cls}`} style={{ fontSize: '0.7rem' }}>{meta.label}</span>
                                                                        <button className="btn btn-secondary btn-icon btn-sm" title="Copier le SID" onClick={() => { navigator.clipboard.writeText(bc.copy_bundle_sid); notify.success('Bundle SID copié'); }}>
                                                                            <Icons.Copy className="w-4 h-4" />
                                                                        </button>
                                                                    </div>
                                                                );
                                                            })}
                                                        </div>
                                                    )}
                                                </div>
                                            </div>
                                        );
                                    })}
                                </div>
                            )}
                        </div>

                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '1rem' }}>Lignes du client ({clientLines.length})</h3>
                            {loadingLines ? (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                    {Array.from({ length: 3 }).map((_, i) => (
                                        <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.6rem 0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px solid var(--border)' }}>
                                            <Skeleton width={16} height={16} circle />
                                            <div style={{ flex: 1 }}><Skeleton width="40%" height={11} style={{ marginBottom: '0.3rem' }} /><Skeleton width="60%" height={9} /></div>
                                            <Skeleton width={60} height={18} />
                                        </div>
                                    ))}
                                </div>
                            ) : clientLines.length === 0 ? (
                                <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)' }}>Aucune ligne associée à ce client.</p>
                            ) : (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                    {clientLines.map(l => {
                                        const curTw = allTwilioAccounts.find(a => a.id === l.twilio_account_id);
                                        const curTwLabel = curTw
                                            ? (curTw.name || curTw.account_sid)
                                            : (l.twilio_account_id ? `#${l.twilio_account_id}` : '— aucun —');
                                        const selectVal = transferTargetByLine[l.id] || '';
                                        const isBusy = transferring === l.id;
                                        return (
                                            <div key={l.id} style={{ padding: '0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px solid var(--border)' }}>
                                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
                                                    <Icons.Phone className="w-4 h-4" style={{ color: 'var(--primary)', flexShrink: 0 }} />
                                                    <div style={{ flex: 1 }}>
                                                        <div style={{ fontWeight: 600, fontSize: '0.85rem' }}>{l.phone_number}</div>
                                                        <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{l.label || '-'}</div>
                                                    </div>
                                                    <span className={`badge ${l.is_active ? 'badge-success' : 'badge-gray'}`}>{l.is_active ? 'Active' : 'Inactive'}</span>
                                                    <span style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{l.total_calls ?? 0} appels</span>
                                                </div>
                                                <div style={{ marginTop: '0.6rem', paddingTop: '0.6rem', borderTop: '1px dashed var(--border)', display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>
                                                    <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)' }}>Compte Twilio (DB) :</div>
                                                    <span style={{ fontSize: '0.78rem', fontWeight: 600, fontFamily: 'ui-monospace, monospace', padding: '0.15rem 0.5rem', background: 'var(--bg-primary)', borderRadius: 4, border: '1px solid var(--border)' }}>
                                                        {curTwLabel}
                                                    </span>
                                                    {curTw?.account_sid && (
                                                        <span style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontFamily: 'ui-monospace, monospace' }}>{curTw.account_sid}</span>
                                                    )}
                                                    {(() => {
                                                        const loc = twLocateByLine[l.id];
                                                        if (!loc) return null;
                                                        if (loc.loading) return <span style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>· vérif. Twilio…</span>;
                                                        if (loc.error)   return <span style={{ fontSize: '0.7rem', color: 'var(--danger, #c00)' }}>· {loc.error}</span>;
                                                        if (loc.actual_account_id == null) {
                                                            return <span className="badge badge-warning" style={{ fontSize: '0.7rem' }}>⚠ Introuvable chez Twilio</span>;
                                                        }
                                                        if (loc.in_sync) {
                                                            return <span className="badge badge-success" style={{ fontSize: '0.7rem' }}>✓ Sync Twilio</span>;
                                                        }
                                                        return (
                                                            <>
                                                                <span className="badge badge-danger" style={{ fontSize: '0.7rem' }}>⚠ Désync Twilio</span>
                                                                <span style={{ fontSize: '0.72rem', color: 'var(--text-muted)' }}>réel :</span>
                                                                <span style={{ fontSize: '0.78rem', fontWeight: 600, fontFamily: 'ui-monospace, monospace', padding: '0.15rem 0.5rem', background: '#fef3c7', color: '#92400e', borderRadius: 4, border: '1px solid #fcd34d' }}>
                                                                    {loc.actual_account_label}
                                                                </span>
                                                                <span style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontFamily: 'ui-monospace, monospace' }}>{loc.actual_account_sid}</span>
                                                                <button className="btn btn-warning btn-sm" onClick={() => reconcileLineTwilio(l)} title="Mettre à jour la DB pour refléter la réalité Twilio (n'agit pas sur Twilio)">
                                                                    Réconcilier DB
                                                                </button>
                                                            </>
                                                        );
                                                    })()}
                                                    <button className="btn btn-secondary btn-sm" onClick={() => locateLineTwilio(l.id)} title="Re-vérifier l'emplacement chez Twilio">↻</button>
                                                    <button className="btn btn-secondary btn-sm" onClick={async () => {
                                                        try {
                                                            const r = await api.get(`/api/lines/${l.id}/twilio-scan`);
                                                            if (r?.error) { setInfoModal({ title: 'Erreur scan', text: r.error }); return; }
                                                            const rows = (r.scan || []).map(s => {
                                                                const tag = s.found ? 'TROUVE' : (s.http_status === 200 ? '-' : `ERR HTTP ${s.http_status}`);
                                                                const sid = s.pn_sid ? ` PN=${s.pn_sid}` : '';
                                                                const returned = s.account_sid_returned ? ` account_sid_returned=${s.account_sid_returned}` : '';
                                                                const err = s.error ? ` err=${s.error.slice(0,200)}` : '';
                                                                return `[id=${s.account_id}] ${s.name}\n  account_sid=${s.account_sid} client=${s.client_id ?? '-'} active=${s.is_active} dbOwner=${s.is_db_owner}\n  ${tag}${sid}${returned}${err}`;
                                                            }).join('\n\n');
                                                            const text = `Scan Twilio pour ${r.phone_number}\nDB twilio_account_id = ${r.db_twilio_account_id}\n\n${rows}\n\n---\nJSON brut :\n${JSON.stringify(r, null, 2)}`;
                                                            setInfoModal({ title: `Scan Twilio — ${r.phone_number}`, text });
                                                        } catch (e) { setInfoModal({ title: 'Erreur scan', text: e.message }); }
                                                    }} title="Scanner tous les comptes Twilio connus en DB pour ce numéro">🔎 Scan</button>
                                                    <div style={{ flex: 1 }} />
                                                    <select
                                                        value={selectVal}
                                                        disabled={isBusy}
                                                        onChange={e => setTransferTargetByLine(s => ({ ...s, [l.id]: e.target.value }))}
                                                        style={{ fontSize: '0.75rem', padding: '0.25rem 0.5rem', borderRadius: 4, border: '1px solid var(--border)' }}
                                                    >
                                                        <option value="">→ Transférer vers…</option>
                                                        {allTwilioAccounts
                                                            .filter(a => {
                                                                if (!a.is_active) return false;
                                                                const loc = twLocateByLine[l.id];
                                                                const realId = loc?.actual_account_id ?? l.twilio_account_id;
                                                                return a.id !== realId;
                                                            })
                                                            .map(a => (
                                                                <option key={a.id} value={a.id}>
                                                                    {(a.name || a.account_sid)}{a.client_id ? ` (client #${a.client_id})` : ' (parent)'}
                                                                </option>
                                                            ))}
                                                    </select>
                                                    <button
                                                        className="btn btn-secondary btn-sm"
                                                        disabled={!selectVal || isBusy}
                                                        onClick={() => transferLineToAccount(l, selectVal)}
                                                        title="Déplace le numéro chez Twilio (IncomingPhoneNumber.AccountSid) puis met à jour la DB"
                                                    >
                                                        {isBusy ? 'Transfert…' : 'Transférer'}
                                                    </button>
                                                </div>
                                            </div>
                                        );
                                    })}
                                </div>
                            )}
                        </div>

                        <div className="card" style={{ padding: '1.5rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '1rem' }}>Derniers appels</h3>
                            {loadingCalls ? <SkeletonTable rows={5} cols={6} /> : calls.length === 0 ? (
                                <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)' }}>Aucun appel récent.</p>
                            ) : (
                                <div className="table-container">
                                    <table className="data-table">
                                        <thead><tr><th>Date</th><th>Ligne</th><th>De</th><th>Vers</th><th>Durée</th><th>Statut</th></tr></thead>
                                        <tbody>
                                            {calls.slice(0, 10).map(call => (
                                                <tr key={call.id}>
                                                    <td style={{ fontSize: '0.8rem' }}>{formatDate(call.created_at)}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{call.line_phone || '-'}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{call.caller_number || '-'}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{call.called_number || '-'}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{call.duration ? `${call.duration}s` : '-'}</td>
                                                    <td><span className={`badge ${call.status === 'completed' ? 'badge-success' : call.status === 'busy' || call.status === 'no-answer' ? 'badge-warning' : 'badge-gray'}`} style={{ fontSize: '0.7rem' }}>{call.status}</span></td>
                                                </tr>
                                            ))}
                                        </tbody>
                                    </table>
                                </div>
                            )}
                        </div>
                    </>
                )}

                {tab === 'widget' && (
                    <>
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700 }}>Widgets du client</h3>
                                <button className="btn btn-primary btn-sm" onClick={() => { setShowCreateWidget(true); setNewWidgetLabel(''); setNewWidgetLineIds(clientLines.map(l => l.id)); }}>
                                    <Icons.Plus className="w-4 h-4" /> Créer un widget
                                </button>
                            </div>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '1rem', lineHeight: 1.5 }}>
                                Chaque widget est une clé d'accès avec des lignes spécifiques. Le widget n'affichera que les lignes sélectionnées.
                            </p>

                            {widgets.length === 0 ? (
                                <div style={{ textAlign: 'center', padding: '2rem', color: 'var(--text-muted)', fontSize: '0.85rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px dashed var(--border)' }}>
                                    Aucun widget créé pour ce client. Cliquez sur "Créer un widget" pour commencer.
                                </div>
                            ) : (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                                    {widgets.map(w => {
                                        const wLines = clientLines.filter(l => w.line_ids?.includes(l.id));
                                        return (
                                            <div key={w.id} className="card" style={{ padding: '1rem', border: `1px solid ${w.is_active ? '#bbf7d0' : 'var(--border)'}` }}>
                                                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.75rem' }}>
                                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                                        <span style={{ fontWeight: 700, fontSize: '0.9rem' }}>{w.label || 'Widget'}</span>
                                                        <span className={`badge ${w.is_active ? 'badge-success' : 'badge-gray'}`} style={{ fontSize: '0.65rem' }}>{w.is_active ? 'Actif' : 'Inactif'}</span>
                                                    </div>
                                                    <div style={{ display: 'flex', gap: '0.25rem' }}>
                                                        <button className="btn btn-secondary btn-sm" onClick={() => toggleWidgetActive(w)} style={{ fontSize: '0.7rem', padding: '0.2rem 0.5rem' }}>
                                                            {w.is_active ? 'Désactiver' : 'Activer'}
                                                        </button>
                                                        <button className="btn btn-danger btn-icon btn-sm" onClick={() => deleteWidget(w)} style={{ padding: '0.2rem 0.4rem' }}>
                                                            <Icons.Trash className="w-3.5 h-3.5" />
                                                        </button>
                                                    </div>
                                                </div>

                                                <div style={{ marginBottom: '0.75rem' }}>
                                                    <div style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', marginBottom: '0.4rem' }}>Lignes incluses</div>
                                                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.35rem' }}>
                                                        {clientLines.map(l => {
                                                            const isIncluded = w.line_ids?.includes(l.id);
                                                            return (
                                                                <button key={l.id}
                                                                    onClick={() => {
                                                                        const newIds = isIncluded
                                                                            ? w.line_ids.filter(id => id !== l.id)
                                                                            : [...(w.line_ids || []), l.id];
                                                                        updateWidgetLines(w.id, newIds);
                                                                    }}
                                                                    style={{ padding: '0.25rem 0.6rem', fontSize: '0.75rem', fontWeight: 600, borderRadius: '1rem', border: `1px solid ${isIncluded ? 'var(--primary)' : 'var(--border)'}`, background: isIncluded ? 'var(--primary)' : 'transparent', color: isIncluded ? 'white' : 'var(--text-secondary)', cursor: 'pointer', transition: 'all 0.15s' }}>
                                                                    {l.phone_number} {l.label ? `(${l.label})` : ''}
                                                                </button>
                                                            );
                                                        })}
                                                    </div>
                                                </div>

                                                <div style={{ marginBottom: '0.5rem' }}>
                                                    <div style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', marginBottom: '0.4rem' }}>Code d'intégration</div>
                                                    <div style={{ position: 'relative' }}>
                                                        <pre style={{ background: '#1e293b', color: '#e2e8f0', padding: '0.75rem', borderRadius: '0.5rem', fontSize: '0.72rem', lineHeight: 1.6, overflowX: 'auto', whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace', border: '1px solid #334155', margin: 0 }}>{getWidgetSnippet(w)}</pre>
                                                        <button className="btn btn-secondary btn-sm" onClick={() => copyToClipboard(getWidgetSnippet(w), w.id)}
                                                            style={{ position: 'absolute', top: '0.5rem', right: '0.5rem', fontSize: '0.7rem', padding: '0.2rem 0.5rem' }}>
                                                            {copied === w.id ? <><Icons.Check className="w-3 h-3" /> Copié</> : <><Icons.Copy className="w-3 h-3" /> Copier</>}
                                                        </button>
                                                    </div>
                                                </div>

                                                <div style={{ display: 'flex', gap: '0.75rem', fontSize: '0.72rem', color: 'var(--text-muted)', marginTop: '0.5rem' }}>
                                                    <span>Créé le {formatDate(w.created_at)}</span>
                                                    {w.last_used_at && <span>Dernière utilisation: {formatDate(w.last_used_at)}</span>}
                                                    <span>{w.calls_count ?? 0} appels</span>
                                                </div>
                                            </div>
                                        );
                                    })}
                                </div>
                            )}
                        </div>
                    </>
                )}
            </div>

            {showCreateWidget && (
                <div className="modal-overlay" onClick={() => setShowCreateWidget(false)}>
                    <div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: '480px' }}>
                        <div className="modal-header">
                            <h2 className="modal-title">Créer un widget</h2>
                            <button className="modal-close" onClick={() => setShowCreateWidget(false)}>&times;</button>
                        </div>
                        <div className="modal-body">
                            <div className="form-group">
                                <label className="form-label">Nom du widget *</label>
                                <input className="form-input" value={newWidgetLabel} onChange={(e) => setNewWidgetLabel(e.target.value)} placeholder="Ex: Widget réception" autoFocus />
                            </div>
                            <div className="form-group">
                                <label className="form-label">Lignes à inclure *</label>
                                <p style={{ fontSize: '0.78rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }}>Sélectionnez les lignes qui seront accessibles depuis ce widget.</p>
                                {clientLines.length === 0 ? (
                                    <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', fontStyle: 'italic' }}>Aucune ligne associée à ce client.</p>
                                ) : (
                                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem', maxHeight: '250px', overflowY: 'auto' }}>
                                        {clientLines.map(l => {
                                            const checked = newWidgetLineIds.includes(l.id);
                                            return (
                                                <label key={l.id} style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', padding: '0.5rem 0.6rem', background: checked ? 'rgba(99, 102, 241, 0.08)' : 'var(--bg-secondary)', borderRadius: '0.5rem', border: `1px solid ${checked ? 'var(--primary)' : 'var(--border)'}`, cursor: 'pointer', transition: 'all 0.15s' }}>
                                                    <input type="checkbox" checked={checked} onChange={() => toggleLineForWidget(l.id)} style={{ accentColor: 'var(--primary)' }} />
                                                    <Icons.Phone className="w-3.5 h-3.5" style={{ color: 'var(--primary)', flexShrink: 0 }} />
                                                    <div style={{ flex: 1 }}>
                                                        <div style={{ fontWeight: 600, fontSize: '0.82rem' }}>{l.phone_number}</div>
                                                        {l.label && <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)' }}>{l.label}</div>}
                                                    </div>
                                                    <span className={`badge ${l.is_active ? 'badge-success' : 'badge-gray'}`} style={{ fontSize: '0.65rem' }}>{l.is_active ? 'Active' : 'Inactive'}</span>
                                                </label>
                                            );
                                        })}
                                    </div>
                                )}
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowCreateWidget(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={createWidget} disabled={creatingWidget}>
                                {creatingWidget ? 'Création...' : `Créer (${newWidgetLineIds.length} ligne${newWidgetLineIds.length > 1 ? 's' : ''})`}
                            </button>
                        </div>
                    </div>
                </div>
            )}
            <InfoModalView />
        </>
    );
};

// ==================== AUDIO PLAYER ====================
// Zone de dépôt audio réutilisable : drag & drop + sélecteur de fichier classique.
// Si `value` est défini, affiche directement l'AudioPlayer.
const AudioDropzone = ({ value, onChange, disabled, notify, hint, maxSizeMB = 10 }) => {
    const [uploading, setUploading] = useState(false);
    const [dragOver, setDragOver] = useState(false);
    const inputRef = useRef(null);
    const ACCEPT = 'audio/mpeg,audio/mp3,audio/wav,audio/ogg,audio/m4a,audio/x-m4a,audio/webm';
    const ACCEPT_EXT = /\.(mp3|wav|ogg|m4a|webm)$/i;

    const upload = async (file) => {
        if (!file) return;
        const isAudio = (file.type && file.type.startsWith('audio/')) || ACCEPT_EXT.test(file.name);
        if (!isAudio) { notify?.error?.('Format audio non supporté'); return; }
        if (file.size > maxSizeMB * 1024 * 1024) { notify?.error?.(`Fichier trop volumineux (max ${maxSizeMB} Mo)`); return; }
        setUploading(true);
        try {
            const fd = new FormData(); fd.append('file', file);
            const res = await api.upload('/api/audio/upload', fd);
            if (res?.url) { onChange(res.url); notify?.success?.('Audio uploadé'); }
            else { notify?.error?.(res?.error || 'Erreur upload'); }
        } catch (err) { notify?.error?.('Erreur upload'); }
        finally { setUploading(false); }
    };

    const onFileInput = async (e) => {
        const file = e.target.files && e.target.files[0];
        await upload(file);
        try { e.target.value = ''; } catch (_) {}
    };
    const onDrop = async (e) => {
        e.preventDefault(); e.stopPropagation(); setDragOver(false);
        if (disabled || uploading) return;
        await upload(e.dataTransfer.files && e.dataTransfer.files[0]);
    };
    const onDragEnter = (e) => { e.preventDefault(); e.stopPropagation(); if (!disabled && !uploading) setDragOver(true); };
    const onDragOver  = (e) => { e.preventDefault(); e.stopPropagation(); if (!disabled && !uploading) setDragOver(true); };
    const onDragLeave = (e) => { e.preventDefault(); e.stopPropagation(); setDragOver(false); };

    if (value) return <AudioPlayer url={value} onRemove={() => onChange('')} />;

    return (
        <div>
            <div
                onClick={() => !disabled && !uploading && inputRef.current?.click()}
                onDragEnter={onDragEnter}
                onDragOver={onDragOver}
                onDragLeave={onDragLeave}
                onDrop={onDrop}
                style={{
                    border: `2px dashed ${dragOver ? 'var(--primary, #7c3aed)' : 'var(--border)'}`,
                    background: dragOver ? 'rgba(124,58,237,0.06)' : 'var(--bg-secondary, #f8fafc)',
                    borderRadius: '0.5rem',
                    padding: '0.75rem 0.9rem',
                    textAlign: 'center',
                    cursor: disabled || uploading ? 'not-allowed' : 'pointer',
                    opacity: disabled ? 0.55 : 1,
                    transition: 'border-color 120ms ease, background 120ms ease',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    gap: '0.5rem',
                    minHeight: 56,
                }}
            >
                <span style={{ fontSize: '1rem', lineHeight: 1 }}>{uploading ? '⏳' : '🎵'}</span>
                <div style={{ textAlign: 'left' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600 }}>
                        {uploading ? 'Upload en cours…' : (dragOver ? 'Relâcher pour uploader' : 'Glisser un fichier ou cliquer')}
                    </div>
                    <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)', marginTop: 1 }}>
                        MP3, WAV, OGG, M4A — max {maxSizeMB} Mo
                    </div>
                </div>
            </div>
            <input ref={inputRef} type="file" accept={ACCEPT} disabled={disabled || uploading}
                onChange={onFileInput} style={{ display: 'none' }} />
            {hint && <small style={{ display: 'block', marginTop: '0.4rem', fontSize: '0.7rem', color: 'var(--text-muted)' }}>{hint}</small>}
        </div>
    );
};

const AudioPlayer = ({ url, onRemove }) => {
    const [playing, setPlaying] = useState(false);
    const audioRef = useRef(null);

    useEffect(() => {
        const a = new Audio(url);
        audioRef.current = a;
        a.addEventListener('ended', () => setPlaying(false));
        return () => { a.pause(); a.removeEventListener('ended', () => setPlaying(false)); };
    }, [url]);

    const toggle = () => {
        const a = audioRef.current;
        if (!a) return;
        if (playing) { a.pause(); setPlaying(false); }
        else { a.play().catch(() => {}); setPlaying(true); }
    };

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.4rem 0.6rem', background: '#f0fdf4', borderRadius: '0.375rem', border: '1px solid #bbf7d0' }}>
            <button type="button" onClick={toggle}
                style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0, display: 'flex', alignItems: 'center' }}>
                {playing ? (
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="#16a34a" stroke="none"><rect x="6" y="4" width="4" height="16" rx="1" /><rect x="14" y="4" width="4" height="16" rx="1" /></svg>
                ) : (
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="#16a34a" stroke="none"><polygon points="5,3 19,12 5,21" /></svg>
                )}
            </button>
            <span style={{ fontSize: '0.75rem', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#166534' }}>{url.split('/').pop()}</span>
            <button type="button" onClick={onRemove}
                style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#dc2626', fontSize: '0.7rem', fontWeight: 600 }}>Retirer</button>
        </div>
    );
};

// ==================== IVR BUILDER ====================
const IvrOptionEditor = ({ option, index, onChange, onRemove, onUpload, uploading }) => {
    const { notify } = useApp();
    const handleField = (field, value) => {
        onChange(index, { ...option, [field]: value });
    };

    return (
        <div style={{ background: 'var(--bg-white)', border: '1px solid var(--border)', borderRadius: '0.5rem', padding: '0.75rem', position: 'relative' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.5rem' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                    <span style={{ width: 28, height: 28, borderRadius: '0.375rem', background: 'var(--primary)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.8rem', fontWeight: 700, flexShrink: 0 }}>
                        {option.digit}
                    </span>
                    <input className="form-input" value={option.label} placeholder="Nom de l'option"
                        onChange={(e) => handleField('label', e.target.value)}
                        style={{ margin: 0, fontWeight: 600, fontSize: '0.85rem', flex: 1 }} />
                </div>
                <button type="button" onClick={() => onRemove(index)}
                    style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', padding: '0.25rem' }}>
                    <Icons.Trash className="w-4 h-4" />
                </button>
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.5rem', marginBottom: '0.5rem' }}>
                <div>
                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Touche</label>
                    <select className="form-input" value={option.digit} onChange={(e) => handleField('digit', e.target.value)} style={{ margin: 0 }}>
                        {[1,2,3,4,5,6,7,8,9,0].map(d => <option key={d} value={d}>{d}</option>)}
                    </select>
                </div>
                <div>
                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Action</label>
                    <select className="form-input" value={option.action} onChange={(e) => handleField('action', e.target.value)} style={{ margin: 0 }}>
                        <option value="forward">Redirection</option>
                        <option value="play">Lire un audio</option>
                        <option value="voicemail">Messagerie</option>
                        <option value="repeat">Répéter le menu</option>
                    </select>
                </div>
            </div>

            {option.action === 'forward' && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                    <div>
                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Numero principal</label>
                        <input className="form-input" type="tel" value={option.number || ''} placeholder="+41 79..."
                            onChange={(e) => handleField('number', e.target.value)} style={{ margin: 0 }} />
                    </div>
                    <div>
                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Fallback (si pas de reponse)</label>
                        <input className="form-input" type="tel" value={option.fallback_number || ''} placeholder="+41 79... (optionnel)"
                            onChange={(e) => handleField('fallback_number', e.target.value)} style={{ margin: 0 }} />
                    </div>
                </div>
            )}

            {option.action === 'play' && (
                <div>
                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Fichier audio</label>
                    <AudioDropzone value={option.audio_url} notify={notify}
                        onChange={(url) => handleField('audio_url', url)} />
                </div>
            )}

            {option.action === 'voicemail' && (
                <div>
                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Message (texte lu)</label>
                    <input className="form-input" value={option.message || ''} placeholder="Veuillez laisser un message..."
                        onChange={(e) => handleField('message', e.target.value)} style={{ margin: 0 }} />
                </div>
            )}
        </div>
    );
};

const IvrBuilder = ({ config, onChange }) => {
    const { notify } = useApp();
    const [uploading, setUploading] = useState(false);

    const safeConfig = config || { welcome: '', welcome_audio: '', options: [] };
    const options = safeConfig.options || [];

    const updateConfig = (newConfig) => {
        onChange({ ...safeConfig, ...newConfig });
    };

    const addOption = () => {
        const usedDigits = options.map(o => String(o.digit));
        const nextDigit = [1,2,3,4,5,6,7,8,9,0].find(d => !usedDigits.includes(String(d))) || 1;
        updateConfig({
            options: [...options, { digit: nextDigit, label: '', action: 'forward', number: '', fallback_number: '', audio_url: '', message: '' }],
        });
    };

    const updateOption = (index, updated) => {
        const newOpts = [...options];
        newOpts[index] = updated;
        updateConfig({ options: newOpts });
    };

    const removeOption = (index) => {
        updateConfig({ options: options.filter((_, i) => i !== index) });
    };

    const handleUpload = async (optionIndex, file) => {
        setUploading(true);
        try {
            const fd = new FormData();
            fd.append('file', file);
            const resp = await fetch(`${CONFIG.API_URL}/api/audio/upload`, {
                method: 'POST',
                headers: { 'Authorization': `Bearer ${api.token}` },
                body: fd,
            });
            const data = await resp.json();
            if (data.url) {
                updateOption(optionIndex, { ...options[optionIndex], audio_url: data.url });
                notify.success('Audio uploade');
            } else {
                notify.error(data.error || 'Erreur upload');
            }
        } catch (e) {
            notify.error('Erreur upload: ' + e.message);
        } finally {
            setUploading(false);
        }
    };

    const handleWelcomeUpload = async (file) => {
        setUploading(true);
        try {
            const fd = new FormData();
            fd.append('file', file);
            const resp = await fetch(`${CONFIG.API_URL}/api/audio/upload`, {
                method: 'POST',
                headers: { 'Authorization': `Bearer ${api.token}` },
                body: fd,
            });
            const data = await resp.json();
            if (data.url) {
                updateConfig({ welcome_audio: data.url });
                notify.success('Audio d\'accueil uploade');
            } else {
                notify.error(data.error || 'Erreur upload');
            }
        } catch (e) {
            notify.error('Erreur upload: ' + e.message);
        } finally {
            setUploading(false);
        }
    };

    return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
            <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil</label>
                <input className="form-input" value={safeConfig.welcome || ''} placeholder="Bienvenue chez notre entreprise..."
                    onChange={(e) => updateConfig({ welcome: e.target.value })} style={{ margin: 0, marginBottom: '0.5rem' }} />
                <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio d'accueil (remplace le texte)</label>
                <AudioDropzone value={safeConfig.welcome_audio} notify={notify}
                    onChange={(url) => updateConfig({ welcome_audio: url })} />
            </div>

            <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Options du menu ({options.length})</div>

            {options.map((opt, i) => (
                <IvrOptionEditor key={i} option={opt} index={i}
                    onChange={updateOption} onRemove={removeOption}
                    onUpload={handleUpload} uploading={uploading} />
            ))}

            {options.length < 10 && (
                <button type="button" className="btn btn-secondary btn-sm" onClick={addOption}
                    style={{ alignSelf: 'flex-start' }}>
                    <Icons.Plus className="w-4 h-4" /> Ajouter une option
                </button>
            )}
        </div>
    );
};

// ==================== LINES ====================
// ============================================================
// LineConfigForm : formulaire complet "ligne" (créa / édition).
// Utilisé dans la modal de LinesView ET dans le tab Configuration
// de LineDetailView. Si inline=true, on n'affiche que le contenu
// (pas le wrapping modal). onSaved(updatedLine) est appelé après PUT/POST.
// ============================================================
const defaultLineForm = {
    phone_number: '', label: '', type: 'forward', forward_number: '',
    fallback_number_1: '', fallback_number_2: '', fallback_number_3: '',
    client_id: '', twilio_account_id: '', description: '',
    is_active: true, caller_id_mode: 'caller',
    ivr_config: { welcome: '', welcome_audio: '', options: [] },
    link_url: '', link_send_sms: false, link_send_email: false, link_send_whatsapp: false,
    link_sms_sender: 'Vocal', link_email: '',
    voice_welcome_message: '', welcome_audio: '', welcome_enabled: false,
    webhook_url: '',
    webhook_url_fallback: '',
    webhook_timeout_ms: 3000,
    forward_fallback_to_voicemail: false,
    voicemail_greeting: '', voicemail_greeting_audio: '', voicemail_email: '',
    email_on_call: false,
    forward_timeout_seconds: 20, fallback_timeout_seconds: 20,
    record_calls: false, recording_disclaimer_enabled: false, recording_disclaimer_text: '',
    transcription_enabled: false, transcription_email: '', transcription_language: 'fr',
};

function buildFormFromLine(line) {
    if (!line) return { ...defaultLineForm, ivr_config: { ...defaultLineForm.ivr_config, options: [] } };
    return {
        phone_number: line.phone_number || '',
        label: line.label || '',
        type: line.type || 'forward',
        forward_number: line.forward_number || '',
        fallback_number_1: line.fallback_number_1 || '',
        fallback_number_2: line.fallback_number_2 || '',
        fallback_number_3: line.fallback_number_3 || '',
        client_id: line.client_id ? String(line.client_id) : '',
        twilio_account_id: line.twilio_account_id ? String(line.twilio_account_id) : '',
        description: line.description || '',
        is_active: line.is_active === 1 || line.is_active === true,
        caller_id_mode: line.caller_id_mode || 'caller',
        ivr_config: (() => { try { return typeof line.ivr_config === 'string' ? JSON.parse(line.ivr_config) : (line.ivr_config || defaultLineForm.ivr_config); } catch (e) { return defaultLineForm.ivr_config; } })(),
        link_url: line.link_url || '',
        link_send_sms: line.link_send_sms === 1 || line.link_send_sms === true,
        link_send_email: line.link_send_email === 1 || line.link_send_email === true,
        link_send_whatsapp: line.link_send_whatsapp === 1 || line.link_send_whatsapp === true,
        link_sms_sender: line.link_sms_sender || 'Vocal',
        link_email: line.link_email || '',
        voice_welcome_message: line.voice_welcome_message || '',
        welcome_audio: line.welcome_audio || '',
        welcome_enabled: line.welcome_enabled === 1 || line.welcome_enabled === true,
        webhook_url: line.webhook_url || '',
        webhook_url_fallback: line.webhook_url_fallback || '',
        webhook_timeout_ms: line.webhook_timeout_ms || 3000,
        forward_fallback_to_voicemail: line.forward_fallback_to_voicemail === 1 || line.forward_fallback_to_voicemail === true,
        voicemail_greeting: line.voicemail_greeting || '',
        voicemail_greeting_audio: line.voicemail_greeting_audio || '',
        voicemail_email: line.voicemail_email || '',
        email_on_call: line.email_on_call === 1 || line.email_on_call === true,
        forward_timeout_seconds: line.forward_timeout_seconds || 20,
        fallback_timeout_seconds: line.fallback_timeout_seconds || 20,
        record_calls: line.record_calls === 1 || line.record_calls === true,
        recording_disclaimer_enabled: line.recording_disclaimer_enabled === 1 || line.recording_disclaimer_enabled === true,
        recording_disclaimer_text: line.recording_disclaimer_text || '',
        transcription_enabled: line.transcription_enabled === 1 || line.transcription_enabled === true,
        transcription_email: line.transcription_email || '',
        transcription_language: line.transcription_language || 'fr',
    };
}

const LineConfigForm = ({ line, clients = [], twilioAccounts = [], inline = false, onSaved, onCancel, hideFields = [] }) => {
    const hidden = (k) => hideFields.includes(k);
    const { notify } = useApp();
    const isEdit = !!line;
    const [form, setForm] = useState(() => buildFormFromLine(line));
    const [saving, setSaving] = useState(false);

    // Re-sync si la prop line change (ex : navigation entre lignes dans le détail).
    useEffect(() => { setForm(buildFormFromLine(line)); }, [line?.id]);

    const handleSave = async (e) => {
        if (e && e.preventDefault) e.preventDefault();
        if (!form.phone_number) { notify.error('Numéro requis'); return; }
        setSaving(true);
        try {
            const payload = {
                ...form,
                client_id: form.client_id ? parseInt(form.client_id) : null,
                twilio_account_id: form.twilio_account_id ? parseInt(form.twilio_account_id) : null,
                is_active: form.is_active ? 1 : 0,
                link_send_sms: form.link_send_sms ? 1 : 0,
                link_send_email: form.link_send_email ? 1 : 0,
                link_send_whatsapp: form.link_send_whatsapp ? 1 : 0,
                forward_fallback_to_voicemail: form.forward_fallback_to_voicemail ? 1 : 0,
                email_on_call: form.email_on_call ? 1 : 0,
                welcome_enabled: form.welcome_enabled ? 1 : 0,
                record_calls: form.record_calls ? 1 : 0,
                recording_disclaimer_enabled: form.recording_disclaimer_enabled ? 1 : 0,
                transcription_enabled: form.transcription_enabled ? 1 : 0,
                forward_timeout_seconds: Math.max(5, Math.min(120, parseInt(form.forward_timeout_seconds) || 20)),
                fallback_timeout_seconds: Math.max(5, Math.min(120, parseInt(form.fallback_timeout_seconds) || 20)),
            };
            let saved = null;
            if (isEdit) {
                const res = await api.put(`/api/lines/${line.id}`, payload);
                if (res?.error) { notify.error('Erreur: ' + res.error); return; }
                notify.success('Ligne mise à jour avec succès');
                if (res?.transfer) {
                    if (res.transfer.transferred) {
                        notify.success(`Numéro transféré sur le subaccount ${res.transfer.to_account_sid}`);
                    } else if (res.transfer.skipped && res.transfer.skipped !== 'no_subaccount_for_client' && res.transfer.skipped !== 'already_on_subaccount') {
                        notify.error(`Transfert ignoré: ${res.transfer.skipped}`);
                    } else if (res.transfer.twilio_message || res.transfer.error) {
                        notify.error(`Transfert Twilio échoué: ${res.transfer.twilio_message || res.transfer.error}`);
                    }
                }
                saved = res?.line || { ...(line || {}), ...payload };
            } else {
                const res = await api.post('/api/lines', payload);
                notify.success('Ligne créée');
                saved = res?.line || res;
            }
            if (onSaved) onSaved(saved);
        } catch (err) {
            notify.error('Erreur: ' + (err.message || 'inconnue'));
        } finally {
            setSaving(false);
        }
    };

    const body = (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
            {!hidden('phone_number') && (
                <div className="form-group">
                    <label className="form-label">Numéro Twilio *</label>
                    <input className="form-input" type="tel" value={form.phone_number} placeholder="+41 44 123 45 67"
                        onChange={(e) => setForm(f => ({ ...f, phone_number: e.target.value }))} required />
                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Format E.164 (ex: +41441234567)</small>
                </div>
            )}
            <div className="form-group">
                <label className="form-label">Libellé</label>
                <input className="form-input" value={form.label} placeholder="Ex: Ligne support"
                    onChange={(e) => setForm(f => ({ ...f, label: e.target.value }))} />
            </div>
            {!hidden('client_id') && (
                <div className="form-group">
                    <label className="form-label">Client assigné</label>
                    <select className="form-input" value={form.client_id} onChange={(e) => setForm(f => ({ ...f, client_id: e.target.value }))}>
                        <option value="">- Non assignée -</option>
                        {clients.map(c => <option key={c.id} value={c.id}>{c.name} ({c.email})</option>)}
                    </select>
                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Les clients apparaissent ici dès leur première connexion</small>
                </div>
            )}
            {!hidden('twilio_account_id') && (
                <div className="form-group">
                    <label className="form-label">Compte Twilio</label>
                    <select className="form-input" value={form.twilio_account_id} onChange={(e) => setForm(f => ({ ...f, twilio_account_id: e.target.value }))}>
                        <option value="">- Non assigné -</option>
                        {twilioAccounts.filter(a => a.is_active).map(a => <option key={a.id} value={a.id}>{a.name} ({a.account_sid})</option>)}
                    </select>
                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Compte Twilio utilisé pour les appels sortants (API) de cette ligne</small>
                </div>
            )}
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                <div className="form-group">
                    <label className="form-label">Type</label>
                    <select className="form-input" value={form.type} onChange={(e) => setForm(f => ({ ...f, type: e.target.value }))}>
                        <option value="forward">Redirection</option>
                        <option value="ivr">Menu IVR</option>
                        <option value="voice_ai">IA Vocale</option>
                        <option value="voicemail">Messagerie</option>
                        <option value="browser">Navigateur (WebRTC)</option>
                        <option value="link">Envoi de lien</option>
                        <option value="webhook">Webhook</option>
                        <option value="external_api">API externe</option>
                    </select>
                </div>
                {form.type === 'forward' && (
                    <>
                        <div className="form-group">
                            <label className="form-label">Numéro de redirection</label>
                            <input className="form-input" type="tel" value={form.forward_number} placeholder="+41 79..."
                                onChange={(e) => setForm(f => ({ ...f, forward_number: e.target.value }))} />
                        </div>
                        <div className="form-group">
                            <label className="form-label">Durée de sonnerie du numéro principal (secondes)</label>
                            <input className="form-input" type="number" min="5" max="120" step="1"
                                value={form.forward_timeout_seconds}
                                onChange={(e) => setForm(f => ({ ...f, forward_timeout_seconds: e.target.value }))} />
                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>
                                Avant de passer au fallback suivant. Défaut : 20s. Max : 120s.
                            </small>
                        </div>
                        <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                            <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Message d'accueil (joué avant la redirection)</div>
                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                <input type="checkbox" checked={!!form.welcome_enabled}
                                    onChange={(e) => setForm(f => ({ ...f, welcome_enabled: e.target.checked }))} />
                                <span>Activer un message d'accueil avant le transfert</span>
                            </label>
                            {form.welcome_enabled && (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                    <div className="form-group" style={{ margin: 0 }}>
                                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Texte (lu par voix synthétique fr-FR)</label>
                                        <input className="form-input" value={form.voice_welcome_message}
                                            placeholder="Bonjour, vous êtes bien chez X. Je vous mets en relation, merci de patienter."
                                            onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))}
                                            style={{ margin: 0 }} />
                                    </div>
                                    <div className="form-group" style={{ margin: 0 }}>
                                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un fichier audio (prioritaire sur le texte)</label>
                                        <AudioDropzone value={form.welcome_audio} notify={notify}
                                            onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))}
                                            hint="Astuce : pour générer un audio professionnel, utilisez le Studio voix (studio.helvia.app) puis uploadez le MP3 ici." />
                                    </div>
                                </div>
                            )}
                        </div>
                    </>
                )}
            </div>
            {form.type === 'forward' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, marginBottom: '0.5rem', color: 'var(--text-secondary)' }}>Numeros de fallback</div>
                    <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', display: 'block', marginBottom: '0.75rem' }}>Si le numéro principal ne répond pas ({form.forward_timeout_seconds || 20}s), les numéros suivants sont essayés dans l'ordre.</small>
                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                        {[1, 2, 3].map(n => (
                            <div key={n} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                <span style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', width: '14px', textAlign: 'center', flexShrink: 0 }}>{n}</span>
                                <input className="form-input" type="tel"
                                    value={form[`fallback_number_${n}`]}
                                    placeholder={`Fallback ${n} - +41 79...`}
                                    onChange={(e) => setForm(f => ({ ...f, [`fallback_number_${n}`]: e.target.value }))}
                                    style={{ margin: 0 }} />
                            </div>
                        ))}
                    </div>
                    <div style={{ marginTop: '0.75rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <label style={{ fontSize: '0.75rem', color: 'var(--text-secondary)', fontWeight: 600, flex: 1 }}>
                            Durée de sonnerie pour chaque fallback (secondes)
                        </label>
                        <input className="form-input" type="number" min="5" max="120" step="1"
                            value={form.fallback_timeout_seconds}
                            onChange={(e) => setForm(f => ({ ...f, fallback_timeout_seconds: e.target.value }))}
                            style={{ margin: 0, width: '90px' }} />
                    </div>
                </div>
            )}
            {form.type === 'forward' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Messagerie vocale (si aucune redirection ne répond)</div>
                    <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                        <input type="checkbox" checked={!!form.forward_fallback_to_voicemail}
                            onChange={(e) => setForm(f => ({ ...f, forward_fallback_to_voicemail: e.target.checked }))} />
                        <span>Activer la messagerie vocale après échec de la redirection</span>
                    </label>
                    {form.forward_fallback_to_voicemail && (
                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                            <div className="form-group" style={{ margin: 0 }}>
                                <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil (texte, joué avant le bip)</label>
                                <input className="form-input" value={form.voicemail_greeting}
                                    placeholder="Votre correspondant n'est pas disponible. Laissez un message après le bip."
                                    onChange={(e) => setForm(f => ({ ...f, voicemail_greeting: e.target.value }))}
                                    style={{ margin: 0 }} />
                            </div>
                            <div className="form-group" style={{ margin: 0 }}>
                                <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio uploadé (joué avant le bip, remplace le texte)</label>
                                <AudioDropzone value={form.voicemail_greeting_audio} notify={notify}
                                    onChange={(url) => setForm(f => ({ ...f, voicemail_greeting_audio: url }))} />
                            </div>
                        </div>
                    )}
                    <div style={{ borderTop: '1px solid var(--border)', paddingTop: '0.75rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                            <input type="checkbox" checked={!!form.email_on_call}
                                onChange={(e) => setForm(f => ({ ...f, email_on_call: e.target.checked }))} />
                            <span>Toujours recevoir l'appel par email (+ message vocal le cas échéant)</span>
                        </label>
                        <div className="form-group" style={{ margin: 0 }}>
                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Email pour les notifications / messages vocaux</label>
                            <input className="form-input" type="email" value={form.voicemail_email}
                                placeholder="Laisser vide = email du client"
                                onChange={(e) => setForm(f => ({ ...f, voicemail_email: e.target.value }))}
                                style={{ margin: 0 }} />
                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Si vide, on utilise l'email du compte client. L'email contient : infos d'appel + enregistrement/message vocal (si présent).</small>
                        </div>
                    </div>
                </div>
            )}
            {form.type === 'ivr' && (
                <IvrBuilder config={form.ivr_config}
                    onChange={(newConfig) => setForm(f => ({ ...f, ivr_config: newConfig }))} />
            )}
            {form.type === 'webhook' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Webhook</div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil</label>
                        <input className="form-input" value={form.voice_welcome_message} placeholder="Votre appel est en cours de traitement..."
                            onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))} style={{ margin: 0, marginBottom: '0.5rem' }} />
                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio d'accueil (remplace le texte)</label>
                        <AudioDropzone value={form.welcome_audio} notify={notify}
                            onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))} />
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL du Webhook *</label>
                        <input className="form-input" type="url" value={form.webhook_url} placeholder="https://api.example.com/incoming-call"
                            onChange={(e) => setForm(f => ({ ...f, webhook_url: e.target.value }))} style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Un POST sera envoy&eacute; avec les donn&eacute;es de l'appel (from, to, call_sid, line_id)</small>
                    </div>
                </div>
            )}
            {form.type === 'external_api' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>API externe</div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL de l'API *</label>
                        <input className="form-input" type="url" value={form.webhook_url} placeholder="https://api.example.com/voice/incoming"
                            onChange={(e) => setForm(f => ({ ...f, webhook_url: e.target.value }))} style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>L'appel sera redirig&eacute; directement vers cette URL (TwiML attendu en r&eacute;ponse). Aucun message d'accueil ne sera jou&eacute;.</small>
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL de fallback (optionnel)</label>
                        <input className="form-input" type="url" value={form.webhook_url_fallback || ''} placeholder="https://backup.example.com/voice/incoming"
                            onChange={(e) => setForm(f => ({ ...f, webhook_url_fallback: e.target.value }))} style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Utilis&eacute;e si l'URL primaire ne r&eacute;pond pas dans le d&eacute;lai imparti.</small>
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Timeout du healthcheck (ms)</label>
                        <input className="form-input" type="number" min="500" max="5000" step="100" value={form.webhook_timeout_ms || 3000}
                            onChange={(e) => setForm(f => ({ ...f, webhook_timeout_ms: parseInt(e.target.value) || 3000 }))} style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>D&eacute;lai (500-5000 ms) pour consid&eacute;rer l'URL primaire indisponible et basculer. D&eacute;faut&nbsp;: 3000.</small>
                    </div>
                </div>
            )}
            {form.type === 'link' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Configuration du lien</div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil</label>
                        <input className="form-input" value={form.voice_welcome_message} placeholder="Un lien vous a ete envoye. Merci et a bientot."
                            onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))} style={{ margin: 0, marginBottom: '0.5rem' }} />
                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio d'accueil (remplace le texte)</label>
                        <AudioDropzone value={form.welcome_audio} notify={notify}
                            onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))} />
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>URL de destination</label>
                        <input className="form-input" type="url" value={form.link_url} placeholder="https://example.com/page"
                            onChange={(e) => setForm(f => ({ ...f, link_url: e.target.value }))} style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Le lien trackable up.helvia.app redirigera vers cette URL</small>
                    </div>
                    <div style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', marginTop: '0.25rem' }}>Canaux d'envoi</div>
                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                            <input type="checkbox" checked={form.link_send_sms} onChange={(e) => setForm(f => ({ ...f, link_send_sms: e.target.checked }))} />
                            <span style={{ fontWeight: 500 }}>SMS</span>
                        </label>
                        {form.link_send_sms && (
                            <div style={{ marginLeft: '1.5rem' }}>
                                <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Expediteur SMS (max 11 car.)</label>
                                <input className="form-input" value={form.link_sms_sender} maxLength={11} placeholder="Vocal"
                                    onChange={(e) => setForm(f => ({ ...f, link_sms_sender: e.target.value.substring(0, 11) }))} style={{ margin: 0, maxWidth: '180px' }} />
                            </div>
                        )}
                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                            <input type="checkbox" checked={form.link_send_email} onChange={(e) => setForm(f => ({ ...f, link_send_email: e.target.checked }))} />
                            <span style={{ fontWeight: 500 }}>Email</span>
                        </label>
                        {form.link_send_email && (
                            <div style={{ paddingLeft: '1.5rem' }}>
                                <input className="form-input" type="email" value={form.link_email} placeholder="destinataire@exemple.ch"
                                    onChange={(e) => setForm(f => ({ ...f, link_email: e.target.value }))} style={{ margin: 0, maxWidth: '260px' }} />
                            </div>
                        )}
                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                            <input type="checkbox" checked={form.link_send_whatsapp} onChange={(e) => setForm(f => ({ ...f, link_send_whatsapp: e.target.checked }))} />
                            <span style={{ fontWeight: 500 }}>WhatsApp</span>
                        </label>
                    </div>
                </div>
            )}
            {form.type === 'voicemail' && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Annonce de la messagerie</div>
                    <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', marginTop: '-0.25rem' }}>
                        Joué à l'appelant avant le bip d'enregistrement. Si l'audio est uploadé, il est prioritaire sur le texte.
                    </small>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Texte d'annonce (voix synthétique fr-FR)</label>
                        <input className="form-input" value={form.voicemail_greeting}
                            placeholder="Bonjour, vous êtes bien chez X. Laissez-nous un message après le bip."
                            onChange={(e) => setForm(f => ({ ...f, voicemail_greeting: e.target.value }))}
                            style={{ margin: 0 }} />
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un fichier audio (prioritaire sur le texte)</label>
                        <AudioDropzone value={form.voicemail_greeting_audio} notify={notify}
                            onChange={(url) => setForm(f => ({ ...f, voicemail_greeting_audio: url }))}
                            hint="Astuce : pour générer un audio professionnel, utilisez le Studio voix (studio.helvia.app) puis uploadez le MP3 ici." />
                    </div>
                    <div className="form-group" style={{ margin: 0 }}>
                        <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Email pour les messages vocaux</label>
                        <input className="form-input" type="email" value={form.voicemail_email}
                            placeholder="Laisser vide = email du client"
                            onChange={(e) => setForm(f => ({ ...f, voicemail_email: e.target.value }))}
                            style={{ margin: 0 }} />
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Si vide, on utilise l'email du compte client.</small>
                    </div>
                </div>
            )}
            <div className="form-group">
                <label className="form-label">Description</label>
                <textarea className="form-input" rows="2" value={form.description} placeholder="Notes internes..."
                    onChange={(e) => setForm(f => ({ ...f, description: e.target.value }))} />
            </div>
            <div className="form-group">
                <label className="form-label">Caller ID affiché</label>
                <select className="form-input" value={form.caller_id_mode} onChange={(e) => setForm(f => ({ ...f, caller_id_mode: e.target.value }))}>
                    <option value="caller">Numéro de l'appelant</option>
                    <option value="line">Numéro de la ligne</option>
                </select>
                <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Quel numéro afficher lors d'un transfert d'appel</small>
            </div>
            {(form.type === 'forward' || form.type === 'ivr' || form.type === 'voicemail' || form.type === 'queue' || form.type === 'voice_bot' || form.type === 'webhook') && (
                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                    <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Enregistrement &amp; transcription</div>
                    {form.type === 'voicemail' ? (
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', marginTop: '-0.3rem' }}>
                            En mode messagerie, l'enregistrement du message est implicite (sinon il n'y aurait rien à conserver).
                        </small>
                    ) : (
                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                            <input type="checkbox" checked={!!form.record_calls}
                                onChange={(e) => setForm(f => ({ ...f, record_calls: e.target.checked }))} />
                            <span>Enregistrer les appels (les deux côtés)</span>
                        </label>
                    )}
                    <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                        <input type="checkbox" checked={!!form.transcription_enabled}
                            onChange={(e) => setForm(f => ({ ...f, transcription_enabled: e.target.checked }))} />
                        <span>Transcrire automatiquement (Whisper) + email</span>
                    </label>
                    {form.transcription_enabled && (
                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                            <div className="form-group" style={{ margin: 0 }}>
                                <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Email pour les transcriptions</label>
                                <input className="form-input" type="email" value={form.transcription_email || ''}
                                    placeholder="Laisser vide = email du client"
                                    onChange={(e) => setForm(f => ({ ...f, transcription_email: e.target.value }))}
                                    style={{ margin: 0 }} />
                            </div>
                            <div className="form-group" style={{ margin: 0 }}>
                                <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Langue de transcription</label>
                                <select className="form-input" value={form.transcription_language || 'fr'}
                                    onChange={(e) => setForm(f => ({ ...f, transcription_language: e.target.value }))}
                                    style={{ margin: 0, maxWidth: 220 }}>
                                    <option value="fr">Français</option>
                                    <option value="en">Anglais</option>
                                    <option value="de">Allemand</option>
                                    <option value="it">Italien</option>
                                    <option value="es">Espagnol</option>
                                    <option value="auto">Détection auto</option>
                                </select>
                            </div>
                        </div>
                    )}
                    {form.record_calls && form.type !== 'voicemail' && (
                        <div style={{ borderTop: '1px solid var(--border)', paddingTop: '0.6rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                <input type="checkbox" checked={!!form.recording_disclaimer_enabled}
                                    onChange={(e) => setForm(f => ({ ...f, recording_disclaimer_enabled: e.target.checked }))} />
                                <span>Avertir l'appelant que l'appel est enregistré (LPD/RGPD)</span>
                            </label>
                            {form.recording_disclaimer_enabled && (
                                <div className="form-group" style={{ margin: 0, paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>
                                        Message d'avertissement (lu en fr-FR avant le transfert)
                                    </label>
                                    <input className="form-input" value={form.recording_disclaimer_text || ''}
                                        placeholder="Cet appel peut être enregistré pour des raisons de qualité et de service."
                                        onChange={(e) => setForm(f => ({ ...f, recording_disclaimer_text: e.target.value }))}
                                        style={{ margin: 0 }} />
                                    <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>
                                        Vide = message par défaut. Recommandé pour être en règle avec la LPD (Suisse) et le RGPD (UE).
                                    </small>
                                </div>
                            )}
                        </div>
                    )}
                </div>
            )}
            <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.75rem', background: form.is_active ? '#f0fdf4' : '#fef2f2', borderRadius: '0.5rem', border: `1px solid ${form.is_active ? '#bbf7d0' : '#fecaca'}` }}>
                <label style={{ position: 'relative', display: 'inline-block', width: '44px', height: '24px', flexShrink: 0 }}>
                    <input type="checkbox" checked={form.is_active} onChange={(e) => setForm(f => ({ ...f, is_active: e.target.checked }))}
                        style={{ opacity: 0, width: 0, height: 0 }} />
                    <span style={{ position: 'absolute', cursor: 'pointer', top: 0, left: 0, right: 0, bottom: 0, background: form.is_active ? '#22c55e' : '#d1d5db', borderRadius: '24px', transition: '0.3s' }}>
                        <span style={{ position: 'absolute', height: '18px', width: '18px', left: form.is_active ? '23px' : '3px', bottom: '3px', background: 'white', borderRadius: '50%', transition: '0.3s' }}></span>
                    </span>
                </label>
                <div>
                    <div style={{ fontWeight: 600, fontSize: '0.875rem', color: form.is_active ? '#166534' : '#991b1b' }}>
                        {form.is_active ? 'Ligne active' : 'Ligne inactive'}
                    </div>
                    <div style={{ fontSize: '0.75rem', color: form.is_active ? '#16a34a' : '#dc2626' }}>
                        {form.is_active ? 'Les appels entrants seront traités' : 'Les appels seront rejetés'}
                    </div>
                </div>
            </div>
        </div>
    );

    const footer = (
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.5rem', marginTop: '1.25rem', borderTop: inline ? '1px solid var(--border)' : 'none', paddingTop: inline ? '1rem' : 0 }}>
            {onCancel && <button type="button" className="btn btn-secondary" onClick={onCancel}>Annuler</button>}
            <button type="submit" className="btn btn-primary" disabled={saving}>
                {saving ? 'Enregistrement...' : (isEdit ? 'Mettre à jour' : 'Créer la ligne')}
            </button>
        </div>
    );

    if (inline) {
        return (
            <form onSubmit={handleSave}>
                {body}
                {footer}
            </form>
        );
    }
    return (
        <form onSubmit={handleSave}>
            <div className="modal-body">{body}</div>
            <div className="modal-footer">
                {onCancel && <button type="button" className="btn btn-secondary" onClick={onCancel}>Annuler</button>}
                <button type="submit" className="btn btn-primary" disabled={saving}>
                    {saving ? 'Enregistrement...' : (isEdit ? 'Mettre à jour' : 'Créer la ligne')}
                </button>
            </div>
        </form>
    );
};

// Mini-form pour mettre à jour un seul champ d'une ligne (utilisé dans les tabs Client / Gateway).
const LineFieldUpdate = ({ line, field, label, hint, options, onSaved, parseValue = (v) => (v ? parseInt(v) : null) }) => {
    const { notify } = useApp();
    const [value, setValue] = useState(line?.[field] != null ? String(line[field]) : '');
    const [saving, setSaving] = useState(false);
    useEffect(() => { setValue(line?.[field] != null ? String(line[field]) : ''); }, [line?.id, field]);

    const save = async (e) => {
        e.preventDefault();
        setSaving(true);
        try {
            const payload = { [field]: parseValue(value) };
            const res = await api.put(`/api/lines/${line.id}`, payload);
            notify.success('Mis à jour');
            if (res?.transfer) {
                if (res.transfer.transferred) notify.success(`Numéro transféré sur ${res.transfer.to_account_sid}`);
                else if (res.transfer.twilio_message || res.transfer.error) notify.error(`Transfert Twilio: ${res.transfer.twilio_message || res.transfer.error}`);
            }
            if (onSaved) onSaved({ ...line, [field]: parseValue(value) });
        } catch (err) { notify.error('Erreur: ' + (err.message || 'inconnue')); }
        finally { setSaving(false); }
    };

    return (
        <form onSubmit={save} style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
            <div className="form-group" style={{ margin: 0 }}>
                <label className="form-label">{label}</label>
                <select className="form-input" value={value} onChange={(e) => setValue(e.target.value)}>
                    <option value="">- Non assigné -</option>
                    {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
                {hint && <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>{hint}</small>}
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                <button type="submit" className="btn btn-primary" disabled={saving}>
                    {saving ? 'Enregistrement...' : 'Enregistrer'}
                </button>
            </div>
        </form>
    );
};

// Filtre les comptes Twilio "principaux" (AC*) actifs pour la commande de ligne.
// On exclut les bundles, sub-test etc.
const activeMainTwilioAccounts = (accs) => (accs || []).filter(a =>
    a && a.is_active && typeof a.account_sid === 'string' && a.account_sid.startsWith('AC')
);

const OrderLineModal = ({ clients, twilioAccounts, onClose, onOrdered, notify }) => {
    const mainAccounts = activeMainTwilioAccounts(twilioAccounts);
    const [twilioAccountId, setTwilioAccountId] = useState(mainAccounts[0]?.id || '');
    const [country, setCountry] = useState('CH');
    const [areaCode, setAreaCode] = useState('22');
    const [areaCodes, setAreaCodes] = useState([]);
    const [numbers, setNumbers] = useState([]);
    const [selectedNumber, setSelectedNumber] = useState(null);
    const [clientId, setClientId] = useState('');
    const [label, setLabel] = useState('');
    const [loading, setLoading] = useState(false);
    const [ordering, setOrdering] = useState(false);
    const [err, setErr] = useState('');

    const COUNTRY_DEFAULTS = { CH: '22', FR: '1' };
    const FR_REGIONS = [
        { id: 'IDF', label: 'Île-de-France', national: '01' },
        { id: 'NO',  label: 'Nord-Ouest',    national: '02' },
        { id: 'NE',  label: 'Nord-Est',      national: '03' },
        { id: 'SE',  label: 'Sud-Est',       national: '04' },
        { id: 'SO',  label: 'Sud-Ouest',     national: '05' },
    ];
    const COUNTRY_FALLBACKS = {
        CH: [
            { code: '22', label: 'Genève', display: '022', region: 'GE' },
            { code: '21', label: 'Vaud', display: '021', region: 'VD' },
            { code: '24', label: 'Yverdon / Riviera', display: '024', region: 'VD' },
            { code: '27', label: 'Valais', display: '027', region: 'VS' },
            { code: '32', label: 'Neuchâtel / Jura', display: '032', region: 'NE' },
        ],
        FR: [
            { code: '1', label: 'Île-de-France (large)', display: '01', region: 'IDF' },
            { code: '2', label: 'Nord-Ouest (large)', display: '02', region: 'NO' },
            { code: '3', label: 'Nord-Est (large)', display: '03', region: 'NE' },
            { code: '4', label: 'Sud-Est (large)', display: '04', region: 'SE' },
            { code: '5', label: 'Sud-Ouest (large)', display: '05', region: 'SO' },
        ],
    };

    const load = useCallback(async (ac, ctry) => {
        setLoading(true);
        setErr('');
        setSelectedNumber(null);
        setNumbers([]);
        try {
            const qs = new URLSearchParams({ area_code: ac, country: ctry });
            if (twilioAccountId) qs.set('twilio_account_id', String(twilioAccountId));
            const r = await api.get(`/api/admin/numbers/available?${qs.toString()}`);
            if (r?.error) { setErr(r.error); return; }
            setNumbers(r.numbers || []);
            if (Array.isArray(r.area_codes) && r.area_codes.length) setAreaCodes(r.area_codes);
        } catch (e) {
            setErr('Erreur de chargement : ' + (e?.message || 'inconnue'));
        } finally {
            setLoading(false);
        }
    }, [twilioAccountId]);

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

    const switchCountry = (c) => {
        if (c === country) return;
        setCountry(c);
        setAreaCode(COUNTRY_DEFAULTS[c] || '');
        setAreaCodes([]);
    };

    const fmtPhone = (e164) => {
        const ch = (e164 || '').match(/^\+41(\d{2})(\d{3})(\d{2})(\d{2})$/);
        if (ch) return `+41 ${ch[1]} ${ch[2]} ${ch[3]} ${ch[4]}`;
        const fr = (e164 || '').match(/^\+33(\d)(\d{2})(\d{2})(\d{2})(\d{2})$/);
        if (fr) return `+33 ${fr[1]} ${fr[2]} ${fr[3]} ${fr[4]} ${fr[5]}`;
        return e164;
    };

    const order = async () => {
        if (!selectedNumber) return;
        setOrdering(true);
        setErr('');
        try {
            const payload = {
                phone_number: selectedNumber.phone_number,
                twilio_account_id: twilioAccountId || undefined,
                client_id: clientId ? parseInt(clientId, 10) : undefined,
                label: label.trim() || undefined,
            };
            const r = await api.post('/api/admin/lines/order', payload);
            if (r?.error) {
                setErr(r.twilio_message || r.error);
                notify.error('Commande échouée : ' + (r.twilio_message || r.error));
                return;
            }
            notify.success(`Ligne ${fmtPhone(r.line.phone_number)} commandée`);
            onOrdered();
        } catch (e) {
            setErr('Erreur : ' + (e?.message || 'inconnue'));
        } finally {
            setOrdering(false);
        }
    };

    return (
        <div className="modal-overlay" onClick={(e) => e.target === e.currentTarget && onClose()}>
            <div className="modal-content" style={{ maxWidth: '720px' }}>
                <div className="modal-header">
                    <h3 className="modal-title">Commander une ligne</h3>
                    <button className="modal-close" onClick={onClose}>&times;</button>
                </div>
                <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                    {/* Compte Twilio sur lequel acheter */}
                    <div className="form-group">
                        <label className="form-label">Compte Twilio</label>
                        <select className="form-input" value={twilioAccountId}
                            onChange={e => setTwilioAccountId(e.target.value ? parseInt(e.target.value, 10) : '')}>
                            <option value="">— Auto (1er compte AC* actif) —</option>
                            {mainAccounts.map(a => (
                                <option key={a.id} value={a.id}>
                                    {a.name || a.account_sid} · {a.account_sid}
                                </option>
                            ))}
                        </select>
                        <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>
                            Le numéro sera acheté sur ce compte. Les sous-comptes (AC* non principal) ne sont pas listés ici.
                        </small>
                    </div>

                    {/* Sélecteur de pays */}
                    <div className="form-group">
                        <label className="form-label">Pays</label>
                        <div style={{ display: 'flex', gap: '0.4rem' }}>
                            {[
                                { code: 'CH', label: 'Suisse', flag: '🇨🇭' },
                                { code: 'FR', label: 'France', flag: '🇫🇷' },
                            ].map(c => {
                                const active = c.code === country;
                                return (
                                    <button key={c.code} type="button" onClick={() => switchCountry(c.code)}
                                        style={{
                                            padding: '0.55rem 1rem', fontSize: '0.9rem', fontWeight: 700,
                                            border: `1.5px solid ${active ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                            background: active ? 'rgba(99,102,241,0.08)' : '#fff',
                                            color: active ? 'var(--primary, #6366f1)' : 'var(--text)',
                                            borderRadius: 10, cursor: 'pointer',
                                            display: 'inline-flex', alignItems: 'center', gap: 8,
                                        }}>
                                        <span style={{ fontSize: '1.1rem' }}>{c.flag}</span>
                                        <span>{c.label}</span>
                                    </button>
                                );
                            })}
                        </div>
                    </div>

                    {/* Sélecteur d'indicatif :
                          - CH : liste plate des cantons
                          - FR : 2 étapes (région → ville) */}
                    {country === 'CH' ? (
                        <div className="form-group">
                            <label className="form-label">Indicatif / canton</label>
                            <div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
                                {(areaCodes.length ? areaCodes : COUNTRY_FALLBACKS.CH).map(ac => {
                                    const active = ac.code === areaCode;
                                    return (
                                        <button key={ac.code} type="button" onClick={() => setAreaCode(ac.code)}
                                            style={{
                                                padding: '0.5rem 0.9rem', fontSize: '0.85rem', fontWeight: 600,
                                                border: `1px solid ${active ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                                background: active ? 'rgba(99,102,241,0.08)' : '#fff',
                                                color: active ? 'var(--primary, #6366f1)' : 'var(--text)',
                                                borderRadius: 8, cursor: 'pointer',
                                                display: 'inline-flex', alignItems: 'center', gap: 6,
                                            }}>
                                            <span style={{ fontFamily: 'ui-monospace, monospace' }}>{ac.display}</span>
                                            <span style={{ fontSize: '0.72rem', color: active ? 'inherit' : 'var(--text-muted)' }}>{ac.label}</span>
                                        </button>
                                    );
                                })}
                            </div>
                        </div>
                    ) : (() => {
                        const list = areaCodes.length ? areaCodes : COUNTRY_FALLBACKS.FR;
                        const activeAc = list.find(a => a.code === areaCode);
                        const activeRegion = activeAc?.region || 'IDF';
                        const regionAcs = list.filter(a => a.region === activeRegion);
                        const wideCode = list.find(a => a.region === activeRegion && a.code.length === 1);
                        const cityAcs = regionAcs.filter(a => a.code.length === 2);
                        return (
                            <>
                                <div className="form-group">
                                    <label className="form-label">Région</label>
                                    <div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
                                        {FR_REGIONS.map(r => {
                                            const isActive = r.id === activeRegion;
                                            return (
                                                <button key={r.id} type="button"
                                                    onClick={() => {
                                                        const wide = list.find(a => a.region === r.id && a.code.length === 1);
                                                        if (wide) setAreaCode(wide.code);
                                                    }}
                                                    style={{
                                                        padding: '0.5rem 0.9rem', fontSize: '0.85rem', fontWeight: 600,
                                                        border: `1px solid ${isActive ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                                        background: isActive ? 'rgba(99,102,241,0.08)' : '#fff',
                                                        color: isActive ? 'var(--primary, #6366f1)' : 'var(--text)',
                                                        borderRadius: 8, cursor: 'pointer',
                                                        display: 'inline-flex', alignItems: 'center', gap: 6,
                                                    }}>
                                                    <span style={{ fontFamily: 'ui-monospace, monospace' }}>{r.national}</span>
                                                    <span style={{ fontSize: '0.72rem', color: isActive ? 'inherit' : 'var(--text-muted)' }}>{r.label}</span>
                                                </button>
                                            );
                                        })}
                                    </div>
                                </div>

                                <div className="form-group">
                                    <label className="form-label">Ville (optionnel)</label>
                                    <div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
                                        {wideCode && (
                                            <button type="button" onClick={() => setAreaCode(wideCode.code)}
                                                style={{
                                                    padding: '0.5rem 0.9rem', fontSize: '0.85rem', fontWeight: 600,
                                                    border: `1px solid ${areaCode === wideCode.code ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                                    background: areaCode === wideCode.code ? 'rgba(99,102,241,0.08)' : '#fff',
                                                    color: areaCode === wideCode.code ? 'var(--primary, #6366f1)' : 'var(--text)',
                                                    borderRadius: 8, cursor: 'pointer',
                                                    display: 'inline-flex', alignItems: 'center', gap: 6,
                                                }}>
                                                <span>🌐</span>
                                                <span style={{ fontSize: '0.78rem' }}>Toute la région</span>
                                            </button>
                                        )}
                                        {cityAcs.map(ac => {
                                            const active = ac.code === areaCode;
                                            return (
                                                <button key={ac.code} type="button" onClick={() => setAreaCode(ac.code)}
                                                    style={{
                                                        padding: '0.5rem 0.9rem', fontSize: '0.85rem', fontWeight: 600,
                                                        border: `1px solid ${active ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                                        background: active ? 'rgba(99,102,241,0.08)' : '#fff',
                                                        color: active ? 'var(--primary, #6366f1)' : 'var(--text)',
                                                        borderRadius: 8, cursor: 'pointer',
                                                        display: 'inline-flex', alignItems: 'center', gap: 6,
                                                    }}>
                                                    <span style={{ fontFamily: 'ui-monospace, monospace' }}>{ac.display}</span>
                                                    <span style={{ fontSize: '0.72rem', color: active ? 'inherit' : 'var(--text-muted)' }}>{ac.label}</span>
                                                </button>
                                            );
                                        })}
                                    </div>
                                </div>
                            </>
                        );
                    })()}

                    {/* Grille de numéros disponibles */}
                    <div className="form-group">
                        <label className="form-label">Numéros disponibles</label>
                        {loading ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: 'var(--text-muted)', fontSize: '0.85rem' }}>
                                Recherche de numéros disponibles…
                            </div>
                        ) : numbers.length === 0 ? (
                            <div style={{ padding: '1.5rem', textAlign: 'center', color: 'var(--text-muted)', fontSize: '0.85rem', background: '#fafafa', borderRadius: 8 }}>
                                Aucun numéro disponible pour cet indicatif. Essayez-en un autre.
                            </div>
                        ) : (
                            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '0.55rem' }}>
                                {numbers.map(n => {
                                    const isSel = selectedNumber?.phone_number === n.phone_number;
                                    return (
                                        <button key={n.phone_number} type="button" onClick={() => setSelectedNumber(n)}
                                            style={{
                                                padding: '0.8rem 0.85rem',
                                                border: `2px solid ${isSel ? 'var(--primary, #6366f1)' : 'var(--border)'}`,
                                                background: isSel ? 'rgba(99,102,241,0.06)' : '#fff',
                                                borderRadius: 10, cursor: 'pointer', textAlign: 'left',
                                            }}>
                                            <div style={{ fontFamily: 'ui-monospace, monospace', fontWeight: 700, fontSize: '0.95rem' }}>
                                                {fmtPhone(n.phone_number)}
                                            </div>
                                            <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)', marginTop: 2 }}>
                                                {n.locality || n.region || (country === 'FR' ? 'France' : 'Suisse')} · {country === 'FR' ? '🇫🇷' : '🇨🇭'}
                                            </div>
                                        </button>
                                    );
                                })}
                            </div>
                        )}
                    </div>

                    {/* Attribution */}
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                        <div className="form-group" style={{ margin: 0 }}>
                            <label className="form-label">Attribuer à un client (optionnel)</label>
                            <select className="form-input" value={clientId}
                                onChange={e => setClientId(e.target.value)}>
                                <option value="">— Non attribuée —</option>
                                {clients.map(c => (
                                    <option key={c.id} value={c.id}>
                                        {c.client_code ? `${c.client_code} · ` : ''}{c.name || c.email || `#${c.id}`}
                                    </option>
                                ))}
                            </select>
                        </div>
                        <div className="form-group" style={{ margin: 0 }}>
                            <label className="form-label">Libellé (optionnel)</label>
                            <input className="form-input" type="text" value={label}
                                placeholder="Ex: Ligne support" onChange={e => setLabel(e.target.value)} />
                        </div>
                    </div>

                    {err && (
                        <div style={{ padding: '0.65rem 0.85rem', background: '#fee2e2', border: '1px solid #fecaca', color: '#991b1b', borderRadius: 8, fontSize: '0.82rem' }}>
                            {err}
                        </div>
                    )}
                </div>
                <div className="modal-footer" style={{ display: 'flex', justifyContent: 'space-between', gap: '0.75rem', padding: '1rem 1.5rem', borderTop: '1px solid var(--border)' }}>
                    <button className="btn btn-secondary" onClick={onClose} disabled={ordering}>Annuler</button>
                    <button className="btn btn-primary" onClick={order} disabled={!selectedNumber || ordering}>
                        {ordering ? 'Commande en cours…' : (selectedNumber ? `Commander ${fmtPhone(selectedNumber.phone_number)}` : 'Sélectionnez un numéro')}
                    </button>
                </div>
            </div>
        </div>
    );
};

const LinesView = () => {
    const { notify, navigate, setSelectedLine } = useApp();
    const [lines, setLines] = useState([]);
    const [clients, setClients] = useState([]);
    const [twilioAccounts, setTwilioAccounts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [search, setSearch] = useState('');
    const [filterType, setFilterType] = useState('all');
    const [showModal, setShowModal] = useState(false);
    const [editLine, setEditLine] = useState(null);
    const [showOrderModal, setShowOrderModal] = useState(false);
    const defaultIvrConfig = { welcome: '', welcome_audio: '', options: [] };
    const [form, setForm] = useState({ phone_number: '', label: '', type: 'forward', forward_number: '', fallback_number_1: '', fallback_number_2: '', fallback_number_3: '', client_id: '', twilio_account_id: '', description: '', is_active: true, caller_id_mode: 'caller', ivr_config: defaultIvrConfig, link_url: '', link_send_sms: false, link_send_email: false, link_send_whatsapp: false, link_sms_sender: 'Vocal', link_email: '', voice_welcome_message: '', welcome_audio: '', welcome_enabled: false, webhook_url: '', webhook_url_fallback: '', webhook_timeout_ms: 3000, forward_fallback_to_voicemail: false, voicemail_greeting: '', voicemail_greeting_audio: '', voicemail_email: '', email_on_call: false, forward_timeout_seconds: 20, fallback_timeout_seconds: 20, record_calls: false, recording_disclaimer_enabled: false, recording_disclaimer_text: '', transcription_enabled: false, transcription_email: '', transcription_language: 'fr' });
    const [uploadingWelcome, setUploadingWelcome] = useState(false);
    const [uploadingVoicemail, setUploadingVoicemail] = useState(false);
    const [saving, setSaving] = useState(false);

    const loadData = useCallback(async () => {
        setLoading(true);
        try {
            const [linesData, clientsData, twilioData] = await Promise.all([api.get('/api/lines'), api.get('/api/admin/clients'), api.get('/api/twilio-accounts')]);
            setLines(linesData.lines || []);
            setClients(clientsData.clients || []);
            setTwilioAccounts(Array.isArray(twilioData) ? twilioData : []);
        } catch (e) { notify.error('Erreur chargement'); }
        finally { setLoading(false); }
    }, [notify]);

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

    const openCreate = () => {
        setEditLine(null);
        setForm({ phone_number: '', label: '', type: 'forward', forward_number: '', fallback_number_1: '', fallback_number_2: '', fallback_number_3: '', client_id: '', twilio_account_id: '', description: '', is_active: true, caller_id_mode: 'caller', ivr_config: { ...defaultIvrConfig, options: [] }, link_url: '', link_send_sms: false, link_send_email: false, link_send_whatsapp: false, link_sms_sender: 'Vocal', link_email: '', voice_welcome_message: '', welcome_audio: '', welcome_enabled: false, webhook_url: '', webhook_url_fallback: '', webhook_timeout_ms: 3000, forward_fallback_to_voicemail: false, voicemail_greeting: '', voicemail_greeting_audio: '', voicemail_email: '', email_on_call: false, forward_timeout_seconds: 20, fallback_timeout_seconds: 20, record_calls: false, recording_disclaimer_enabled: false, recording_disclaimer_text: '', transcription_enabled: false, transcription_email: '', transcription_language: 'fr' });
        setShowModal(true);
    };

    const openEdit = (line) => {
        setEditLine(line);
        setForm({
            phone_number: line.phone_number || '',
            label: line.label || '',
            type: line.type || 'forward',
            forward_number: line.forward_number || '',
            fallback_number_1: line.fallback_number_1 || '',
            fallback_number_2: line.fallback_number_2 || '',
            fallback_number_3: line.fallback_number_3 || '',
            client_id: line.client_id ? String(line.client_id) : '',
            twilio_account_id: line.twilio_account_id ? String(line.twilio_account_id) : '',
            description: line.description || '',
            is_active: line.is_active === 1 || line.is_active === true,
            caller_id_mode: line.caller_id_mode || 'caller',
            ivr_config: (() => { try { return typeof line.ivr_config === 'string' ? JSON.parse(line.ivr_config) : line.ivr_config || defaultIvrConfig; } catch(e) { return defaultIvrConfig; } })(),
            link_url: line.link_url || '',
            link_send_sms: line.link_send_sms === 1 || line.link_send_sms === true,
            link_send_email: line.link_send_email === 1 || line.link_send_email === true,
            link_send_whatsapp: line.link_send_whatsapp === 1 || line.link_send_whatsapp === true,
            link_sms_sender: line.link_sms_sender || 'Vocal',
            link_email: line.link_email || '',
            voice_welcome_message: line.voice_welcome_message || '',
            welcome_audio: line.welcome_audio || '',
            welcome_enabled: line.welcome_enabled === 1 || line.welcome_enabled === true,
            webhook_url: line.webhook_url || '',
        webhook_url_fallback: line.webhook_url_fallback || '',
        webhook_timeout_ms: line.webhook_timeout_ms || 3000,
            forward_fallback_to_voicemail: line.forward_fallback_to_voicemail === 1 || line.forward_fallback_to_voicemail === true,
            voicemail_greeting: line.voicemail_greeting || '',
            voicemail_greeting_audio: line.voicemail_greeting_audio || '',
            voicemail_email: line.voicemail_email || '',
            email_on_call: line.email_on_call === 1 || line.email_on_call === true,
            forward_timeout_seconds: line.forward_timeout_seconds || 20,
            fallback_timeout_seconds: line.fallback_timeout_seconds || 20,
            record_calls: line.record_calls === 1 || line.record_calls === true,
            recording_disclaimer_enabled: line.recording_disclaimer_enabled === 1 || line.recording_disclaimer_enabled === true,
            recording_disclaimer_text: line.recording_disclaimer_text || '',
            transcription_enabled: line.transcription_enabled === 1 || line.transcription_enabled === true,
            transcription_email: line.transcription_email || '',
            transcription_language: line.transcription_language || 'fr',
        });
        setShowModal(true);
    };

    const handleSave = async (e) => {
        e.preventDefault();
        if (!form.phone_number) { notify.error('Numéro requis'); return; }
        setSaving(true);
        try {
            const payload = { ...form, client_id: form.client_id ? parseInt(form.client_id) : null, twilio_account_id: form.twilio_account_id ? parseInt(form.twilio_account_id) : null, is_active: form.is_active ? 1 : 0, link_send_sms: form.link_send_sms ? 1 : 0, link_send_email: form.link_send_email ? 1 : 0, link_send_whatsapp: form.link_send_whatsapp ? 1 : 0, forward_fallback_to_voicemail: form.forward_fallback_to_voicemail ? 1 : 0, email_on_call: form.email_on_call ? 1 : 0, welcome_enabled: form.welcome_enabled ? 1 : 0, record_calls: form.record_calls ? 1 : 0, recording_disclaimer_enabled: form.recording_disclaimer_enabled ? 1 : 0, transcription_enabled: form.transcription_enabled ? 1 : 0, forward_timeout_seconds: Math.max(5, Math.min(120, parseInt(form.forward_timeout_seconds) || 20)), fallback_timeout_seconds: Math.max(5, Math.min(120, parseInt(form.fallback_timeout_seconds) || 20)) };
            if (editLine) {
                const res = await api.put(`/api/lines/${editLine.id}`, payload);
                if (res?.error) { notify.error('Erreur: ' + res.error); return; }
                notify.success('Ligne mise à jour avec succès');
                if (res?.transfer) {
                    if (res.transfer.transferred) {
                        notify.success(`Numéro transféré sur le subaccount ${res.transfer.to_account_sid}`);
                    } else if (res.transfer.already_on_target) {
                        // silencieux
                    } else if (res.transfer.skipped === 'no_subaccount_for_client') {
                        // silencieux : le client n'a pas de subaccount
                    } else if (res.transfer.skipped) {
                        notify.error(`Transfert ignoré: ${res.transfer.skipped}`);
                    } else if (res.transfer.twilio_message || res.transfer.error) {
                        notify.error(`Transfert Twilio échoué: ${res.transfer.twilio_message || res.transfer.error}`);
                    }
                }
            } else {
                await api.post('/api/lines', payload);
                notify.success('Ligne créée');
            }
            setShowModal(false);
            loadData();
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setSaving(false); }
    };

    const handleDelete = async (line) => {
        if (!confirm(`Supprimer la ligne ${line.phone_number} ?`)) return;
        try {
            await api.del(`/api/lines/${line.id}`);
            notify.success('Ligne supprimée');
            loadData();
        } catch (e) { notify.error('Erreur suppression'); }
    };

    const handleToggle = async (line) => {
        try {
            await api.put(`/api/lines/${line.id}`, { is_active: line.is_active ? 0 : 1 });
            notify.success(line.is_active ? 'Ligne désactivée' : 'Ligne activée');
            loadData();
        } catch (e) { notify.error('Erreur'); }
    };

    const filtered = useMemo(() => {
        let list = lines;
        if (filterType !== 'all') list = list.filter(l => l.type === filterType);
        if (search) {
            const s = search.toLowerCase();
            list = list.filter(l =>
                (l.phone_number || '').toLowerCase().includes(s) ||
                (l.description || l.label || '').toLowerCase().includes(s) ||
                (l.client_name || '').toLowerCase().includes(s)
            );
        }
        return list;
    }, [lines, search, filterType]);

    const typeFilters = [
        { id: 'all', label: 'Toutes' },
        { id: 'forward', label: 'Redirection' },
        { id: 'ivr', label: 'IVR' },
        { id: 'voice_ai', label: 'IA Vocale' },
        { id: 'voicemail', label: 'Messagerie' },
        { id: 'link', label: 'Lien' },
    ];

    const webhookUrl = 'https://api.helvia.app/voice';

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Lignes</h1>
                    <p className="page-subtitle">{lines.length} lignes configurées</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-secondary btn-sm" onClick={loadData}>
                        <Icons.Refresh className="w-4 h-4" /> Actualiser
                    </button>
                    <button className="btn btn-secondary btn-sm" onClick={() => setShowOrderModal(true)}>
                        <Icons.Plus className="w-4 h-4" /> Commander ligne
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={openCreate}>
                        <Icons.Plus className="w-4 h-4" /> Nouvelle ligne
                    </button>
                </div>
            </div>
            <div className="page-content">
                <div style={{ background: 'var(--primary-light, #f0f0ff)', border: '1px solid var(--primary, #6366f1)', borderRadius: '0.75rem', padding: '0.75rem 1rem', marginBottom: '1rem', fontSize: '0.8125rem' }}>
                    <strong>Webhook Twilio :</strong>{' '}
                    <code style={{ background: 'white', padding: '0.15rem 0.5rem', borderRadius: '0.375rem', fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', userSelect: 'all' }}>{webhookUrl}</code>
                    <span style={{ color: 'var(--text-muted)', marginLeft: '0.5rem' }}>→ Configurer dans Twilio &gt; Phone Numbers &gt; Voice &gt; "A Call Comes In"</span>
                </div>

                <div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem', flexWrap: 'wrap', alignItems: 'center' }}>
                    <div style={{ position: 'relative', flex: '1', maxWidth: '320px' }}>
                        <Icons.Search className="w-4 h-4" style={{ position: 'absolute', left: '0.75rem', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted)' }} />
                        <input type="text" value={search} onChange={(e) => setSearch(e.target.value)}
                            placeholder="Rechercher..." className="form-input" style={{ paddingLeft: '2.25rem' }} />
                    </div>
                    <div className="tabs">
                        {typeFilters.map(f => (
                            <button key={f.id} className={`tab ${filterType === f.id ? 'active' : ''}`} onClick={() => setFilterType(f.id)}>
                                {f.label}
                            </button>
                        ))}
                    </div>
                </div>

                {loading ? (
                    <SkeletonTable rows={8} cols={7} />
                ) : filtered.length === 0 ? (
                    <div className="empty-state">
                        <Icons.Phone className="empty-state-icon" />
                        <h3>Aucune ligne</h3>
                        <p>Ajoutez votre premier numéro Twilio puis assignez-le à un client.</p>
                        <button className="btn btn-primary" onClick={openCreate} style={{ marginTop: '1rem' }}>
                            <Icons.Plus className="w-4 h-4" /> Ajouter une ligne
                        </button>
                    </div>
                ) : (
                    <div className="table-container">
                        <table className="data-table">
                            <thead>
                                <tr>
                                    <th>Numéro</th>
                                    <th>Type</th>
                                    <th>Client</th>
                                    <th>Redirection</th>
                                    <th>Statut</th>
                                    <th>Appels</th>
                                    <th>Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {filtered.map((l) => (
                                    <tr key={l.id} style={{ cursor: 'pointer' }} onClick={() => { setSelectedLine(l); navigate('line-detail'); }}>
                                        <td>
                                            <div style={{ fontWeight: 600, fontFamily: 'ui-monospace, monospace', fontSize: '0.8125rem' }}>{l.phone_number}</div>
                                            {(l.label || l.description) && <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{l.label || l.description}</div>}
                                        </td>
                                        <td><span className={`badge ${LINE_TYPES[l.type]?.color || 'badge-gray'}`}>{LINE_TYPES[l.type]?.label || l.type}</span></td>
                                        <td style={{ color: l.client_name ? 'var(--text-primary)' : 'var(--text-muted)', fontWeight: l.client_name ? 500 : 400 }}>
                                            {l.client_name || 'Non assignée'}
                                        </td>
                                        <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
                                            {l.forward_number || '-'}
                                        </td>
                                        <td>
                                            <button onClick={(e) => { e.stopPropagation(); handleToggle(l); }} className={`badge ${l.is_active ? 'badge-success' : 'badge-gray'}`} style={{ cursor: 'pointer', border: 'none' }}>
                                                {l.is_active ? 'Active' : 'Inactive'}
                                            </button>
                                        </td>
                                        <td>{l.total_calls || 0}</td>
                                        <td>
                                            <div style={{ display: 'flex', gap: '0.25rem' }}>
                                                <button className="btn btn-secondary btn-icon btn-sm" onClick={(e) => { e.stopPropagation(); openEdit(l); }}><Icons.Edit className="w-4 h-4" /></button>
                                                <button className="btn btn-danger btn-icon btn-sm" onClick={(e) => { e.stopPropagation(); handleDelete(l); }}><Icons.Trash className="w-4 h-4" /></button>
                                            </div>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {showOrderModal && (
                <OrderLineModal
                    clients={clients}
                    twilioAccounts={twilioAccounts}
                    onClose={() => setShowOrderModal(false)}
                    onOrdered={() => { setShowOrderModal(false); loadData(); }}
                    notify={notify}
                />
            )}

            {showModal && (
                <div className="modal-overlay" onClick={(e) => e.target === e.currentTarget && setShowModal(false)}>
                    <div className="modal-content" style={{ maxWidth: '620px' }}>
                        <div className="modal-header">
                            <h3 className="modal-title">{editLine ? 'Modifier la ligne' : 'Nouvelle ligne'}</h3>
                            <button className="modal-close" onClick={() => setShowModal(false)}>&times;</button>
                        </div>
                        <LineConfigForm
                            line={editLine}
                            clients={clients}
                            twilioAccounts={twilioAccounts}
                            onCancel={() => setShowModal(false)}
                            onSaved={() => { setShowModal(false); loadData(); }}
                        />
                        {false && (<form onSubmit={handleSave}>
                            <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                                <div className="form-group">
                                    <label className="form-label">Numéro Twilio *</label>
                                    <input className="form-input" type="tel" value={form.phone_number} placeholder="+41 44 123 45 67"
                                        onChange={(e) => setForm(f => ({ ...f, phone_number: e.target.value }))} required />
                                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Format E.164 (ex: +41441234567)</small>
                                </div>
                                <div className="form-group">
                                    <label className="form-label">Libellé</label>
                                    <input className="form-input" value={form.label} placeholder="Ex: Ligne support"
                                        onChange={(e) => setForm(f => ({ ...f, label: e.target.value }))} />
                                </div>
                                <div className="form-group">
                                    <label className="form-label">Client assigné</label>
                                    <select className="form-input" value={form.client_id} onChange={(e) => setForm(f => ({ ...f, client_id: e.target.value }))}>
                                        <option value="">- Non assignée -</option>
                                        {clients.map(c => <option key={c.id} value={c.id}>{c.name} ({c.email})</option>)}
                                    </select>
                                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Les clients apparaissent ici dès leur première connexion</small>
                                </div>
                                <div className="form-group">
                                    <label className="form-label">Compte Twilio</label>
                                    <select className="form-input" value={form.twilio_account_id} onChange={(e) => setForm(f => ({ ...f, twilio_account_id: e.target.value }))}>
                                        <option value="">- Non assigné -</option>
                                        {twilioAccounts.filter(a => a.is_active).map(a => <option key={a.id} value={a.id}>{a.name} ({a.account_sid})</option>)}
                                    </select>
                                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Compte Twilio utilisé pour les appels sortants (API) de cette ligne</small>
                                </div>
                                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                                    <div className="form-group">
                                        <label className="form-label">Type</label>
                                        <select className="form-input" value={form.type} onChange={(e) => setForm(f => ({ ...f, type: e.target.value }))}>
                                            <option value="forward">Redirection</option>
                                            <option value="ivr">Menu IVR</option>
                                            <option value="voice_ai">IA Vocale</option>
                                            <option value="voicemail">Messagerie</option>
                                            <option value="browser">Navigateur (WebRTC)</option>
                                            <option value="link">Envoi de lien</option>
                                            <option value="webhook">Webhook</option>
                                            <option value="external_api">API externe</option>
                                        </select>
                                    </div>
                                    {form.type === 'forward' && (
                                        <>
                                            <div className="form-group">
                                                <label className="form-label">Numéro de redirection</label>
                                                <input className="form-input" type="tel" value={form.forward_number} placeholder="+41 79..."
                                                    onChange={(e) => setForm(f => ({ ...f, forward_number: e.target.value }))} />
                                            </div>
                                            <div className="form-group">
                                                <label className="form-label">Durée de sonnerie du numéro principal (secondes)</label>
                                                <input className="form-input" type="number" min="5" max="120" step="1"
                                                    value={form.forward_timeout_seconds}
                                                    onChange={(e) => setForm(f => ({ ...f, forward_timeout_seconds: e.target.value }))} />
                                                <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>
                                                    Avant de passer au fallback suivant. Défaut : 20s. Max : 120s.
                                                </small>
                                            </div>
                                            <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                                <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Message d'accueil (joué avant la redirection)</div>
                                                <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                                    <input type="checkbox" checked={!!form.welcome_enabled}
                                                        onChange={(e) => setForm(f => ({ ...f, welcome_enabled: e.target.checked }))} />
                                                    <span>Activer un message d'accueil avant le transfert</span>
                                                </label>
                                                {form.welcome_enabled && (
                                                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                                        <div className="form-group" style={{ margin: 0 }}>
                                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Texte (lu par voix synthétique fr-FR)</label>
                                                            <input className="form-input" value={form.voice_welcome_message}
                                                                placeholder="Bonjour, vous êtes bien chez X. Je vous mets en relation, merci de patienter."
                                                                onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))}
                                                                style={{ margin: 0 }} />
                                                        </div>
                                                        <div className="form-group" style={{ margin: 0 }}>
                                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un fichier audio (prioritaire sur le texte)</label>
                                                            <AudioDropzone value={form.welcome_audio} notify={notify}
                                                                onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))}
                                                                hint="Astuce : pour générer un audio professionnel, utilisez le Studio voix (studio.helvia.app) puis uploadez le MP3 ici." />
                                                        </div>
                                                    </div>
                                                )}
                                            </div>
                                        </>
                                    )}
                                </div>
                                {form.type === 'forward' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, marginBottom: '0.5rem', color: 'var(--text-secondary)' }}>Numeros de fallback</div>
                                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', display: 'block', marginBottom: '0.75rem' }}>Si le numéro principal ne répond pas ({form.forward_timeout_seconds || 20}s), les numéros suivants sont essayés dans l'ordre.</small>
                                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                            {[1, 2, 3].map(n => (
                                                <div key={n} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                                    <span style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', width: '14px', textAlign: 'center', flexShrink: 0 }}>{n}</span>
                                                    <input className="form-input" type="tel"
                                                        value={form[`fallback_number_${n}`]}
                                                        placeholder={`Fallback ${n} - +41 79...`}
                                                        onChange={(e) => setForm(f => ({ ...f, [`fallback_number_${n}`]: e.target.value }))}
                                                        style={{ margin: 0 }} />
                                                </div>
                                            ))}
                                        </div>
                                        <div style={{ marginTop: '0.75rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                            <label style={{ fontSize: '0.75rem', color: 'var(--text-secondary)', fontWeight: 600, flex: 1 }}>
                                                Durée de sonnerie pour chaque fallback (secondes)
                                            </label>
                                            <input className="form-input" type="number" min="5" max="120" step="1"
                                                value={form.fallback_timeout_seconds}
                                                onChange={(e) => setForm(f => ({ ...f, fallback_timeout_seconds: e.target.value }))}
                                                style={{ margin: 0, width: '90px' }} />
                                        </div>
                                    </div>
                                )}
                                {form.type === 'forward' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Messagerie vocale (si aucune redirection ne répond)</div>
                                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                            <input type="checkbox" checked={!!form.forward_fallback_to_voicemail}
                                                onChange={(e) => setForm(f => ({ ...f, forward_fallback_to_voicemail: e.target.checked }))} />
                                            <span>Activer la messagerie vocale après échec de la redirection</span>
                                        </label>

                                        {form.forward_fallback_to_voicemail && (
                                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                                <div className="form-group" style={{ margin: 0 }}>
                                                    <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil (texte, joué avant le bip)</label>
                                                    <input className="form-input" value={form.voicemail_greeting}
                                                        placeholder="Votre correspondant n'est pas disponible. Laissez un message après le bip."
                                                        onChange={(e) => setForm(f => ({ ...f, voicemail_greeting: e.target.value }))}
                                                        style={{ margin: 0 }} />
                                                </div>
                                                <div className="form-group" style={{ margin: 0 }}>
                                                    <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio uploadé (joué avant le bip, remplace le texte)</label>
                                                    <AudioDropzone value={form.voicemail_greeting_audio} notify={notify}
                                                        onChange={(url) => setForm(f => ({ ...f, voicemail_greeting_audio: url }))} />
                                                </div>
                                            </div>
                                        )}

                                        <div style={{ borderTop: '1px solid var(--border)', paddingTop: '0.75rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                                <input type="checkbox" checked={!!form.email_on_call}
                                                    onChange={(e) => setForm(f => ({ ...f, email_on_call: e.target.checked }))} />
                                                <span>Toujours recevoir l'appel par email (+ message vocal le cas échéant)</span>
                                            </label>
                                            <div className="form-group" style={{ margin: 0 }}>
                                                <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Email pour les notifications / messages vocaux</label>
                                                <input className="form-input" type="email" value={form.voicemail_email}
                                                    placeholder="Laisser vide = email du client"
                                                    onChange={(e) => setForm(f => ({ ...f, voicemail_email: e.target.value }))}
                                                    style={{ margin: 0 }} />
                                                <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Si vide, on utilise l'email du compte client. L'email contient : infos d'appel + enregistrement/message vocal (si présent).</small>
                                            </div>
                                        </div>
                                    </div>
                                )}
                                {form.type === 'ivr' && (
                                    <IvrBuilder config={form.ivr_config}
                                        onChange={(newConfig) => setForm(f => ({ ...f, ivr_config: newConfig }))} />
                                )}
                                {form.type === 'webhook' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Webhook</div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil</label>
                                            <input className="form-input" value={form.voice_welcome_message} placeholder="Votre appel est en cours de traitement..."
                                                onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))} style={{ margin: 0, marginBottom: '0.5rem' }} />
                                            <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio d'accueil (remplace le texte)</label>
                                            <AudioDropzone value={form.welcome_audio} notify={notify}
                                                onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))} />
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL du Webhook *</label>
                                            <input className="form-input" type="url" value={form.webhook_url} placeholder="https://api.example.com/incoming-call"
                                                onChange={(e) => setForm(f => ({ ...f, webhook_url: e.target.value }))} style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Un POST sera envoy&eacute; avec les donn&eacute;es de l'appel (from, to, call_sid, line_id)</small>
                                        </div>
                                    </div>
                                )}
                                {form.type === 'external_api' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>API externe</div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL de l'API *</label>
                                            <input className="form-input" type="url" value={form.webhook_url} placeholder="https://api.example.com/voice/incoming"
                                                onChange={(e) => setForm(f => ({ ...f, webhook_url: e.target.value }))} style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>L'appel sera redirig&eacute; directement vers cette URL (TwiML attendu en r&eacute;ponse). Aucun message d'accueil ne sera jou&eacute;.</small>
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>URL de fallback (optionnel)</label>
                                            <input className="form-input" type="url" value={form.webhook_url_fallback || ''} placeholder="https://backup.example.com/voice/incoming"
                                                onChange={(e) => setForm(f => ({ ...f, webhook_url_fallback: e.target.value }))} style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Utilis&eacute;e si l'URL primaire ne r&eacute;pond pas dans le d&eacute;lai imparti.</small>
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Timeout du healthcheck (ms)</label>
                                            <input className="form-input" type="number" min="500" max="5000" step="100" value={form.webhook_timeout_ms || 3000}
                                                onChange={(e) => setForm(f => ({ ...f, webhook_timeout_ms: parseInt(e.target.value) || 3000 }))} style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>D&eacute;lai (500-5000 ms) pour consid&eacute;rer l'URL primaire indisponible et basculer. D&eacute;faut&nbsp;: 3000.</small>
                                        </div>
                                    </div>
                                )}
                                {form.type === 'link' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Configuration du lien</div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Message d'accueil</label>
                                            <input className="form-input" value={form.voice_welcome_message} placeholder="Un lien vous a ete envoye. Merci et a bientot."
                                                onChange={(e) => setForm(f => ({ ...f, voice_welcome_message: e.target.value }))} style={{ margin: 0, marginBottom: '0.5rem' }} />
                                            <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un audio d'accueil (remplace le texte)</label>
                                            <AudioDropzone value={form.welcome_audio} notify={notify}
                                                onChange={(url) => setForm(f => ({ ...f, welcome_audio: url }))} />
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>URL de destination</label>
                                            <input className="form-input" type="url" value={form.link_url} placeholder="https://example.com/page"
                                                onChange={(e) => setForm(f => ({ ...f, link_url: e.target.value }))} style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Le lien trackable up.helvia.app redirigera vers cette URL</small>
                                        </div>
                                        <div style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', marginTop: '0.25rem' }}>Canaux d'envoi</div>
                                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                                                <input type="checkbox" checked={form.link_send_sms} onChange={(e) => setForm(f => ({ ...f, link_send_sms: e.target.checked }))} />
                                                <span style={{ fontWeight: 500 }}>SMS</span>
                                            </label>
                                            {form.link_send_sms && (
                                                <div style={{ marginLeft: '1.5rem' }}>
                                                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Expediteur SMS (max 11 car.)</label>
                                                    <input className="form-input" value={form.link_sms_sender} maxLength={11} placeholder="Vocal"
                                                        onChange={(e) => setForm(f => ({ ...f, link_sms_sender: e.target.value.substring(0, 11) }))} style={{ margin: 0, maxWidth: '180px' }} />
                                                </div>
                                            )}
                                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                                                <input type="checkbox" checked={form.link_send_email} onChange={(e) => setForm(f => ({ ...f, link_send_email: e.target.checked }))} />
                                                <span style={{ fontWeight: 500 }}>Email</span>
                                            </label>
                                            {form.link_send_email && (
                                                <div style={{ paddingLeft: '1.5rem' }}>
                                                    <input className="form-input" type="email" value={form.link_email} placeholder="destinataire@exemple.ch"
                                                        onChange={(e) => setForm(f => ({ ...f, link_email: e.target.value }))} style={{ margin: 0, maxWidth: '260px' }} />
                                                </div>
                                            )}
                                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', fontSize: '0.85rem' }}>
                                                <input type="checkbox" checked={form.link_send_whatsapp} onChange={(e) => setForm(f => ({ ...f, link_send_whatsapp: e.target.checked }))} />
                                                <span style={{ fontWeight: 500 }}>WhatsApp</span>
                                            </label>
                                        </div>
                                    </div>
                                )}
                                {form.type === 'voicemail' && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Annonce de la messagerie</div>
                                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', marginTop: '-0.25rem' }}>
                                            Joué à l'appelant avant le bip d'enregistrement. Si l'audio est uploadé, il est prioritaire sur le texte.
                                        </small>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Texte d'annonce (voix synthétique fr-FR)</label>
                                            <input className="form-input" value={form.voicemail_greeting}
                                                placeholder="Bonjour, vous êtes bien chez X. Laissez-nous un message après le bip."
                                                onChange={(e) => setForm(f => ({ ...f, voicemail_greeting: e.target.value }))}
                                                style={{ margin: 0 }} />
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Ou un fichier audio (prioritaire sur le texte)</label>
                                            <AudioDropzone value={form.voicemail_greeting_audio} notify={notify}
                                                onChange={(url) => setForm(f => ({ ...f, voicemail_greeting_audio: url }))}
                                                hint="Astuce : pour générer un audio professionnel, utilisez le Studio voix (studio.helvia.app) puis uploadez le MP3 ici." />
                                        </div>
                                        <div className="form-group" style={{ margin: 0 }}>
                                            <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.4rem' }}>Email pour les messages vocaux</label>
                                            <input className="form-input" type="email" value={form.voicemail_email}
                                                placeholder="Laisser vide = email du client"
                                                onChange={(e) => setForm(f => ({ ...f, voicemail_email: e.target.value }))}
                                                style={{ margin: 0 }} />
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>Si vide, on utilise l'email du compte client.</small>
                                        </div>
                                    </div>
                                )}
                                <div className="form-group">
                                    <label className="form-label">Description</label>
                                    <textarea className="form-input" rows="2" value={form.description} placeholder="Notes internes..."
                                        onChange={(e) => setForm(f => ({ ...f, description: e.target.value }))} />
                                </div>
                                <div className="form-group">
                                    <label className="form-label">Caller ID affiché</label>
                                    <select className="form-input" value={form.caller_id_mode} onChange={(e) => setForm(f => ({ ...f, caller_id_mode: e.target.value }))}>
                                        <option value="caller">Numéro de l'appelant</option>
                                        <option value="line">Numéro de la ligne</option>
                                    </select>
                                    <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Quel numéro afficher lors d'un transfert d'appel</small>
                                </div>
                                {(form.type === 'forward' || form.type === 'ivr' || form.type === 'voicemail' || form.type === 'queue' || form.type === 'voice_bot' || form.type === 'webhook') && (
                                    <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>Enregistrement &amp; transcription</div>
                                        {form.type === 'voicemail' ? (
                                            <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem', marginTop: '-0.3rem' }}>
                                                En mode messagerie, l'enregistrement du message est implicite (sinon il n'y aurait rien à conserver).
                                            </small>
                                        ) : (
                                            <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                                <input type="checkbox" checked={!!form.record_calls}
                                                    onChange={(e) => setForm(f => ({ ...f, record_calls: e.target.checked }))} />
                                                <span>Enregistrer les appels (les deux côtés)</span>
                                            </label>
                                        )}
                                        <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                            <input type="checkbox" checked={!!form.transcription_enabled}
                                                onChange={(e) => setForm(f => ({ ...f, transcription_enabled: e.target.checked }))} />
                                            <span>Transcrire automatiquement (Whisper) + email</span>
                                        </label>
                                        {form.transcription_enabled && (
                                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                                <div className="form-group" style={{ margin: 0 }}>
                                                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Email pour les transcriptions</label>
                                                    <input className="form-input" type="email" value={form.transcription_email || ''}
                                                        placeholder="Laisser vide = email du client"
                                                        onChange={(e) => setForm(f => ({ ...f, transcription_email: e.target.value }))}
                                                        style={{ margin: 0 }} />
                                                </div>
                                                <div className="form-group" style={{ margin: 0 }}>
                                                    <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>Langue de transcription</label>
                                                    <select className="form-input" value={form.transcription_language || 'fr'}
                                                        onChange={(e) => setForm(f => ({ ...f, transcription_language: e.target.value }))}
                                                        style={{ margin: 0, maxWidth: 220 }}>
                                                        <option value="fr">Français</option>
                                                        <option value="en">Anglais</option>
                                                        <option value="de">Allemand</option>
                                                        <option value="it">Italien</option>
                                                        <option value="es">Espagnol</option>
                                                        <option value="auto">Détection auto</option>
                                                    </select>
                                                </div>
                                            </div>
                                        )}
                                        {form.record_calls && form.type !== 'voicemail' && (
                                            <div style={{ borderTop: '1px solid var(--border)', paddingTop: '0.6rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                                <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem', cursor: 'pointer' }}>
                                                    <input type="checkbox" checked={!!form.recording_disclaimer_enabled}
                                                        onChange={(e) => setForm(f => ({ ...f, recording_disclaimer_enabled: e.target.checked }))} />
                                                    <span>Avertir l'appelant que l'appel est enregistré (LPD/RGPD)</span>
                                                </label>
                                                {form.recording_disclaimer_enabled && (
                                                    <div className="form-group" style={{ margin: 0, paddingLeft: '1.25rem', borderLeft: '2px solid var(--border)' }}>
                                                        <label style={{ fontSize: '0.7rem', fontWeight: 600, color: 'var(--text-muted)', display: 'block', marginBottom: '0.2rem' }}>
                                                            Message d'avertissement (lu en fr-FR avant le transfert)
                                                        </label>
                                                        <input className="form-input" value={form.recording_disclaimer_text || ''}
                                                            placeholder="Cet appel peut être enregistré pour des raisons de qualité et de service."
                                                            onChange={(e) => setForm(f => ({ ...f, recording_disclaimer_text: e.target.value }))}
                                                            style={{ margin: 0 }} />
                                                        <small style={{ color: 'var(--text-muted)', fontSize: '0.7rem' }}>
                                                            Vide = message par défaut. Recommandé pour être en règle avec la LPD (Suisse) et le RGPD (UE).
                                                        </small>
                                                    </div>
                                                )}
                                            </div>
                                        )}
                                    </div>
                                )}
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.75rem', background: form.is_active ? '#f0fdf4' : '#fef2f2', borderRadius: '0.5rem', border: `1px solid ${form.is_active ? '#bbf7d0' : '#fecaca'}` }}>
                                    <label style={{ position: 'relative', display: 'inline-block', width: '44px', height: '24px', flexShrink: 0 }}>
                                        <input type="checkbox" checked={form.is_active} onChange={(e) => setForm(f => ({ ...f, is_active: e.target.checked }))}
                                            style={{ opacity: 0, width: 0, height: 0 }} />
                                        <span style={{ position: 'absolute', cursor: 'pointer', top: 0, left: 0, right: 0, bottom: 0, background: form.is_active ? '#22c55e' : '#d1d5db', borderRadius: '24px', transition: '0.3s' }}>
                                            <span style={{ position: 'absolute', height: '18px', width: '18px', left: form.is_active ? '23px' : '3px', bottom: '3px', background: 'white', borderRadius: '50%', transition: '0.3s' }}></span>
                                        </span>
                                    </label>
                                    <div>
                                        <div style={{ fontWeight: 600, fontSize: '0.875rem', color: form.is_active ? '#166534' : '#991b1b' }}>
                                            {form.is_active ? 'Ligne active' : 'Ligne inactive'}
                                        </div>
                                        <div style={{ fontSize: '0.75rem', color: form.is_active ? '#16a34a' : '#dc2626' }}>
                                            {form.is_active ? 'Les appels entrants seront traités' : 'Les appels seront rejetés'}
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div className="modal-footer">
                                <button type="button" className="btn btn-secondary" onClick={() => setShowModal(false)}>Annuler</button>
                                <button type="submit" className="btn btn-primary" disabled={saving}>
                                    {saving ? 'Enregistrement...' : (editLine ? 'Mettre à jour' : 'Créer la ligne')}
                                </button>
                            </div>
                        </form>)}
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== SUBSCRIPTIONS ====================
const SubscriptionsView = () => {
    const { notify } = useApp();
    const [subscriptions, setSubscriptions] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        const load = async () => {
            setLoading(true);
            try {
                const data = await api.get('/api/admin/subscriptions');
                setSubscriptions(Array.isArray(data) ? data : data.subscriptions || []);
            } catch (e) { console.error(e); }
            finally { setLoading(false); }
        };
        load();
    }, []);

    const activeSubs = subscriptions.filter(s => s.status === 'active');
    const pendingSubs = subscriptions.filter(s => s.status === 'pending');
    const monthlyRevenue = subscriptions.reduce((sum, s) => s.status === 'active' ? sum + (s.amount || 0) : sum, 0);

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Abonnements</h1>
                    <p className="page-subtitle">Gestion des abonnements clients</p>
                </div>
            </div>
            <div className="page-content">
                <div className="stats-grid" style={{ marginBottom: '1.5rem' }}>
                    <div className="stat-card">
                        <div className="stat-icon green"><Icons.CreditCard className="w-6 h-6" /></div>
                        <div>
                            <div className="stat-label">Actifs</div>
                            <div className="stat-value">{activeSubs.length}</div>
                        </div>
                    </div>
                    <div className="stat-card">
                        <div className="stat-icon orange"><Icons.CreditCard className="w-6 h-6" /></div>
                        <div>
                            <div className="stat-label">En attente</div>
                            <div className="stat-value">{pendingSubs.length}</div>
                        </div>
                    </div>
                    <div className="stat-card">
                        <div className="stat-icon purple"><Icons.BarChart className="w-6 h-6" /></div>
                        <div>
                            <div className="stat-label">Revenus mensuels</div>
                            <div className="stat-value">{formatCurrency(monthlyRevenue)}</div>
                        </div>
                    </div>
                </div>

                {loading ? (
                    <SkeletonTable rows={6} cols={7} />
                ) : subscriptions.length === 0 ? (
                    <div className="empty-state">
                        <Icons.CreditCard className="empty-state-icon" />
                        <h3>Aucun abonnement</h3>
                        <p>Les abonnements apparaîtront ici.</p>
                    </div>
                ) : (
                    <div className="table-container">
                        <table className="data-table">
                            <thead>
                                <tr>
                                    <th>Client</th>
                                    <th>Plan</th>
                                    <th>Statut</th>
                                    <th>Montant</th>
                                    <th>Prochaine facture</th>
                                    <th>Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {subscriptions.map((s) => (
                                    <tr key={s.id}>
                                        <td style={{ fontWeight: 600 }}>{s.client_name || s.email || '-'}</td>
                                        <td>{s.plan || '-'}</td>
                                        <td>
                                            <span className={`badge ${s.status === 'active' ? 'badge-success' : s.status === 'pending' ? 'badge-warning' : 'badge-gray'}`}>
                                                {s.status === 'active' ? 'Actif' : s.status === 'pending' ? 'En attente' : s.status || '-'}
                                            </span>
                                        </td>
                                        <td>{formatCurrency(s.amount)}</td>
                                        <td>{formatDate(s.next_billing)}</td>
                                        <td>
                                            <button className="btn btn-secondary btn-icon btn-sm"><Icons.Edit className="w-4 h-4" /></button>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>
        </>
    );
};

// ==================== TWILIO ACCOUNTS ====================
const TwilioAccountsView = () => {
    const { notify } = useApp();
    const [accounts, setAccounts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [showModal, setShowModal] = useState(false);
    const [editing, setEditing] = useState(null);
    const [form, setForm] = useState({ name: '', account_sid: '', auth_token: '', api_key_sid: '', api_key_secret: '', twiml_app_sid: '' });
    const [showSecrets, setShowSecrets] = useState({});
    const [accountNumbers, setAccountNumbers] = useState({});
    const [loadingNumbers, setLoadingNumbers] = useState({});
    const [showNumbers, setShowNumbers] = useState({});
    const [balances, setBalances] = useState({}); // { [accId]: { balance, currency, error, loading } }

    const loadBalance = async (acc) => {
        setBalances(prev => ({ ...prev, [acc.id]: { ...(prev[acc.id] || {}), loading: true, error: null } }));
        try {
            const data = await api.get(`/api/twilio-accounts/${acc.id}/balance`);
            if (data && data.success) {
                setBalances(prev => ({ ...prev, [acc.id]: { balance: data.balance, currency: data.currency, loading: false } }));
            } else {
                setBalances(prev => ({ ...prev, [acc.id]: { loading: false, error: data?.error || 'Erreur' } }));
            }
        } catch (e) {
            setBalances(prev => ({ ...prev, [acc.id]: { loading: false, error: e.message || 'Erreur' } }));
        }
    };

    const loadAllBalances = async (accs) => {
        // Lance toutes les requêtes en parallèle, sans attendre.
        accs.forEach(a => { if (a.is_active) loadBalance(a); });
    };

    const loadAccountNumbers = async (acc) => {
        setLoadingNumbers(prev => ({ ...prev, [acc.id]: true }));
        try {
            const data = await api.get(`/api/twilio-accounts/${acc.id}/numbers`);
            setAccountNumbers(prev => ({ ...prev, [acc.id]: data.numbers || [] }));
        } catch (e) {
            notify.error(`Erreur chargement numéros (${acc.name}): ${e.message || ''}`);
            setAccountNumbers(prev => ({ ...prev, [acc.id]: [] }));
        } finally {
            setLoadingNumbers(prev => ({ ...prev, [acc.id]: false }));
        }
    };

    const toggleNumbers = (acc) => {
        const next = !showNumbers[acc.id];
        setShowNumbers(prev => ({ ...prev, [acc.id]: next }));
        if (next && accountNumbers[acc.id] === undefined) {
            loadAccountNumbers(acc);
        }
    };

    const loadAccounts = async () => {
        setLoading(true);
        try {
            const data = await api.get('/api/twilio-accounts');
            const accs = Array.isArray(data) ? data : [];
            setAccounts(accs);
            loadAllBalances(accs);
        } catch (e) { notify.error('Erreur chargement'); }
        finally { setLoading(false); }
    };

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

    const openNew = () => {
        setEditing(null);
        setForm({ name: '', account_sid: '', auth_token: '', api_key_sid: '', api_key_secret: '', twiml_app_sid: '' });
        setShowModal(true);
    };

    const openEdit = (acc) => {
        setEditing(acc);
        setForm({
            name: acc.name || '',
            account_sid: acc.account_sid || '',
            auth_token: acc.auth_token || '',
            api_key_sid: acc.api_key_sid || '',
            api_key_secret: acc.api_key_secret || '',
            twiml_app_sid: acc.twiml_app_sid || '',
        });
        setShowModal(true);
    };

    const handleSave = async () => {
        if (!form.name || !form.account_sid || !form.auth_token) {
            notify.error('Nom, Account SID et Auth Token requis');
            return;
        }
        try {
            if (editing) {
                await api.put(`/api/twilio-accounts/${editing.id}`, form);
                notify.success('Compte mis à jour');
            } else {
                await api.post('/api/twilio-accounts', form);
                notify.success('Compte créé');
            }
            setShowModal(false);
            loadAccounts();
        } catch (e) { notify.error('Erreur sauvegarde'); }
    };

    const handleToggle = async (acc) => {
        await api.put(`/api/twilio-accounts/${acc.id}`, { is_active: acc.is_active ? 0 : 1 });
        loadAccounts();
    };

    const handleDelete = async (acc) => {
        if (!confirm(`Supprimer le compte "${acc.name}" ?`)) return;
        const res = await api.del(`/api/twilio-accounts/${acc.id}`);
        if (res.error) { notify.error(res.error); return; }
        notify.success('Compte supprimé');
        loadAccounts();
    };

    const toggleSecret = (id, field) => {
        setShowSecrets(prev => ({ ...prev, [`${id}_${field}`]: !prev[`${id}_${field}`] }));
    };

    const mask = (value) => value ? value.slice(0, 6) + '••••••••' : '-';

    const hasApiKey = (acc) => acc.api_key_sid && acc.api_key_secret;

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Comptes Twilio</h1>
                    <p className="page-subtitle">{accounts.length} compte(s) configuré(s)</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-primary btn-sm" onClick={openNew}>
                        <Icons.Plus className="w-4 h-4" /> Ajouter
                    </button>
                </div>
            </div>
            <div className="page-content">
                {loading ? (
                    <SkeletonCards count={3} columns={1} />
                ) : accounts.length === 0 ? (
                    <div className="empty-state">
                        <Icons.Server className="empty-state-icon" />
                        <h3>Aucun compte Twilio</h3>
                        <p>Ajoutez votre premier compte Twilio pour commencer.</p>
                    </div>
                ) : (
                    <div style={{ display: 'grid', gap: '1rem' }}>
                        {accounts.map(acc => (
                            <div key={acc.id} className="card" style={{ padding: '1.25rem' }}>
                                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '1rem' }}>
                                    <div>
                                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                            <h3 style={{ fontWeight: 700, fontSize: '1rem', margin: 0 }}>{acc.name}</h3>
                                            <span className={`badge ${acc.is_active ? 'badge-success' : 'badge-gray'}`}>
                                                {acc.is_active ? 'Actif' : 'Inactif'}
                                            </span>
                                            {hasApiKey(acc) && (
                                                <span className="badge badge-primary" title="API Key configurée, prêt pour Voice SDK">SDK</span>
                                            )}
                                        </div>
                                        <p style={{ fontSize: '0.8rem', color: '#94a3b8', margin: '0.25rem 0 0' }}>
                                            {acc.lines_count || 0} lignes - {acc.calls_count || 0} appels
                                        </p>
                                        {(() => {
                                            const b = balances[acc.id];
                                            if (!acc.is_active) return null;
                                            if (!b || b.loading) {
                                                return <p style={{ fontSize: '0.78rem', color: '#94a3b8', margin: '0.2rem 0 0', fontStyle: 'italic' }}>Solde Twilio : chargement...</p>;
                                            }
                                            if (b.error) {
                                                return (
                                                    <p style={{ fontSize: '0.78rem', color: '#f59e0b', margin: '0.2rem 0 0', display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
                                                        Solde Twilio : indisponible ({b.error})
                                                        <button onClick={() => loadBalance(acc)} style={{ background: 'none', border: 'none', color: '#3b82f6', cursor: 'pointer', padding: 0, fontSize: '0.72rem', textDecoration: 'underline' }}>réessayer</button>
                                                    </p>
                                                );
                                            }
                                            const value = b.balance != null ? b.balance.toFixed(2) : '0.00';
                                            const low = b.balance != null && b.balance < 10;
                                            return (
                                                <p style={{ fontSize: '0.85rem', margin: '0.3rem 0 0', display: 'flex', alignItems: 'center', gap: '0.4rem', fontWeight: 700, color: low ? '#dc2626' : '#16a34a' }}>
                                                    Solde Twilio : {value} {b.currency || 'USD'}
                                                    {low && <span style={{ fontSize: '0.7rem', fontWeight: 600, background: '#fef2f2', color: '#dc2626', padding: '0.1rem 0.4rem', borderRadius: '999px' }}>BAS</span>}
                                                    <button onClick={() => loadBalance(acc)} title="Rafraîchir" style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0, color: '#9ca3af', fontSize: '0.78rem' }}>↻</button>
                                                </p>
                                            );
                                        })()}
                                    </div>
                                    <div style={{ display: 'flex', gap: '0.5rem' }}>
                                        <button className="btn btn-ghost btn-sm" onClick={() => handleToggle(acc)} title={acc.is_active ? 'Désactiver' : 'Activer'}>
                                            {acc.is_active ? '⏸' : '▶'}
                                        </button>
                                        <button className="btn btn-ghost btn-sm" onClick={() => openEdit(acc)}>
                                            <Icons.Edit className="w-4 h-4" />
                                        </button>
                                        <button className="btn btn-ghost btn-sm" onClick={() => handleDelete(acc)} style={{ color: '#ef4444' }}>
                                            <Icons.Trash className="w-4 h-4" />
                                        </button>
                                    </div>
                                </div>

                                <div style={{ display: 'grid', gap: '0.5rem', fontSize: '0.8rem' }}>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                        <span style={{ color: '#64748b', minWidth: '100px' }}>Account SID</span>
                                        <code style={{ background: '#f1f5f9', padding: '0.2rem 0.5rem', borderRadius: '4px', fontSize: '0.75rem', flex: 1 }}>
                                            {showSecrets[`${acc.id}_sid`] ? acc.account_sid : mask(acc.account_sid)}
                                        </code>
                                        <button onClick={() => toggleSecret(acc.id, 'sid')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px' }}>
                                            {showSecrets[`${acc.id}_sid`] ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                        </button>
                                    </div>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                        <span style={{ color: '#64748b', minWidth: '100px' }}>Auth Token</span>
                                        <code style={{ background: '#f1f5f9', padding: '0.2rem 0.5rem', borderRadius: '4px', fontSize: '0.75rem', flex: 1 }}>
                                            {showSecrets[`${acc.id}_auth`] ? acc.auth_token : mask(acc.auth_token)}
                                        </code>
                                        <button onClick={() => toggleSecret(acc.id, 'auth')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px' }}>
                                            {showSecrets[`${acc.id}_auth`] ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                        </button>
                                    </div>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                        <span style={{ color: '#64748b', minWidth: '100px' }}>API Key SID</span>
                                        <code style={{ background: hasApiKey(acc) ? '#dcfce7' : '#fef3c7', padding: '0.2rem 0.5rem', borderRadius: '4px', fontSize: '0.75rem', flex: 1 }}>
                                            {acc.api_key_sid ? (showSecrets[`${acc.id}_apikey`] ? acc.api_key_sid : mask(acc.api_key_sid)) : 'Non configuré'}
                                        </code>
                                        {acc.api_key_sid && (
                                            <button onClick={() => toggleSecret(acc.id, 'apikey')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px' }}>
                                                {showSecrets[`${acc.id}_apikey`] ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                            </button>
                                        )}
                                    </div>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                        <span style={{ color: '#64748b', minWidth: '100px' }}>API Key Secret</span>
                                        <code style={{ background: acc.api_key_secret ? '#dcfce7' : '#fef3c7', padding: '0.2rem 0.5rem', borderRadius: '4px', fontSize: '0.75rem', flex: 1 }}>
                                            {acc.api_key_secret ? (showSecrets[`${acc.id}_secret`] ? acc.api_key_secret : mask(acc.api_key_secret)) : 'Non configuré'}
                                        </code>
                                        {acc.api_key_secret && (
                                            <button onClick={() => toggleSecret(acc.id, 'secret')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px' }}>
                                                {showSecrets[`${acc.id}_secret`] ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                            </button>
                                        )}
                                    </div>
                                    {acc.twiml_app_sid && (
                                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                            <span style={{ color: '#64748b', minWidth: '100px' }}>TwiML App</span>
                                            <code style={{ background: '#f1f5f9', padding: '0.2rem 0.5rem', borderRadius: '4px', fontSize: '0.75rem', flex: 1 }}>
                                                {showSecrets[`${acc.id}_twiml`] ? acc.twiml_app_sid : mask(acc.twiml_app_sid)}
                                            </code>
                                            <button onClick={() => toggleSecret(acc.id, 'twiml')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px' }}>
                                                {showSecrets[`${acc.id}_twiml`] ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                            </button>
                                        </div>
                                    )}
                                </div>

                                <div style={{ marginTop: '1rem', borderTop: '1px solid var(--border)', paddingTop: '0.75rem' }}>
                                    <button className="btn btn-ghost btn-sm" onClick={() => toggleNumbers(acc)} style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
                                        <Icons.Phone className="w-4 h-4" />
                                        {showNumbers[acc.id] ? 'Masquer' : 'Voir'} les numéros Twilio
                                        {accountNumbers[acc.id] !== undefined ? (
                                            <span style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>({accountNumbers[acc.id].length})</span>
                                        ) : null}
                                        <button
                                            type="button"
                                            onClick={(e) => { e.stopPropagation(); loadAccountNumbers(acc); if (!showNumbers[acc.id]) setShowNumbers(prev => ({ ...prev, [acc.id]: true })); }}
                                            style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0, marginLeft: '0.25rem', color: 'var(--text-muted)' }}
                                            title="Recharger"
                                        >
                                            ↻
                                        </button>
                                    </button>

                                    {showNumbers[acc.id] && (
                                        <div style={{ marginTop: '0.6rem' }}>
                                            {loadingNumbers[acc.id] ? (
                                                <div style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Chargement…</div>
                                            ) : (accountNumbers[acc.id] || []).length === 0 ? (
                                                <div style={{ fontSize: '0.8rem', color: 'var(--text-muted)', padding: '0.5rem', background: 'var(--bg-secondary)', borderRadius: '0.4rem' }}>Aucun numéro sur ce compte.</div>
                                            ) : (
                                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
                                                    {(accountNumbers[acc.id] || []).map(n => (
                                                        <div key={n.sid} style={{ display: 'grid', gridTemplateColumns: 'auto 1fr auto auto', gap: '0.6rem', alignItems: 'center', padding: '0.45rem 0.6rem', background: 'var(--bg-secondary)', borderRadius: '0.4rem', border: '1px solid var(--border)', fontSize: '0.78rem' }}>
                                                            <Icons.Phone className="w-3.5 h-3.5" style={{ color: 'var(--primary)' }} />
                                                            <div style={{ minWidth: 0 }}>
                                                                <div style={{ fontWeight: 600 }}>{n.phone_number}</div>
                                                                <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                                                    {n.friendly_name || '-'}{n.db_line ? ` · DB#${n.db_line.id}` : ''}
                                                                </div>
                                                            </div>
                                                            <code style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>{n.sid}</code>
                                                            {n.db_line ? (
                                                                <span className="badge badge-success" style={{ fontSize: '0.65rem' }}>Vocal</span>
                                                            ) : (
                                                                <span className="badge badge-gray" style={{ fontSize: '0.65rem' }} title="Numéro sur Twilio mais pas en DB Vocal">Orphelin</span>
                                                            )}
                                                        </div>
                                                    ))}
                                                </div>
                                            )}
                                        </div>
                                    )}
                                </div>
                            </div>
                        ))}
                    </div>
                )}
            </div>

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal" onClick={(e) => e.stopPropagation()}>
                        <div className="modal-header">
                            <h2 className="modal-title">{editing ? 'Modifier le compte' : 'Nouveau compte Twilio'}</h2>
                            <button className="modal-close" onClick={() => setShowModal(false)}>
                                <Icons.X className="w-5 h-5" />
                            </button>
                        </div>
                        <div className="modal-body">
                            <div className="form-group">
                                <label className="form-label">Nom du compte *</label>
                                <input type="text" className="form-input" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="Ex: Basical, Yannick..." />
                            </div>
                            <div style={{ borderTop: '1px solid #e2e8f0', margin: '1rem 0', paddingTop: '0.75rem' }}>
                                <p style={{ fontSize: '0.75rem', fontWeight: 600, color: '#64748b', marginBottom: '0.75rem' }}>IDENTIFIANTS TWILIO</p>
                            </div>
                            <div className="form-group">
                                <label className="form-label">Account SID *</label>
                                <input type="text" className="form-input" value={form.account_sid} onChange={(e) => setForm({ ...form, account_sid: e.target.value })} placeholder="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
                            </div>
                            <div className="form-group">
                                <label className="form-label">Auth Token *</label>
                                <input type="text" className="form-input" value={form.auth_token} onChange={(e) => setForm({ ...form, auth_token: e.target.value })} placeholder="Votre auth token Twilio" />
                            </div>
                            <div style={{ borderTop: '1px solid #e2e8f0', margin: '1rem 0', paddingTop: '0.75rem' }}>
                                <p style={{ fontSize: '0.75rem', fontWeight: 600, color: '#64748b', marginBottom: '0.25rem' }}>VOICE SDK (optionnel)</p>
                                <p style={{ fontSize: '0.7rem', color: '#94a3b8', marginBottom: '0.75rem' }}>Requis pour recevoir les appels dans le navigateur</p>
                            </div>
                            <div className="form-group">
                                <label className="form-label">API Key SID</label>
                                <input type="text" className="form-input" value={form.api_key_sid} onChange={(e) => setForm({ ...form, api_key_sid: e.target.value })} placeholder="SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
                            </div>
                            <div className="form-group">
                                <label className="form-label">API Key Secret</label>
                                <input type="text" className="form-input" value={form.api_key_secret} onChange={(e) => setForm({ ...form, api_key_secret: e.target.value })} placeholder="Votre API Key Secret" />
                            </div>
                            <div className="form-group">
                                <label className="form-label">TwiML App SID</label>
                                <input type="text" className="form-input" value={form.twiml_app_sid} onChange={(e) => setForm({ ...form, twiml_app_sid: e.target.value })} placeholder="APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-ghost" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={handleSave}>
                                {editing ? 'Enregistrer' : 'Créer'}
                            </button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== ACCESS ====================
const AccessView = () => {
    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Access & Permissions</h1>
                    <p className="page-subtitle">Gestion des accès et rôles</p>
                </div>
            </div>
            <div className="page-content">
                <div className="empty-state">
                    <Icons.Key className="empty-state-icon" />
                    <h3>Aucun accès configuré</h3>
                    <p>Configurez les permissions et rôles des utilisateurs ici.</p>
                </div>
            </div>
        </>
    );
};

// ==================== ADMINS ====================
const AdminsView = () => {
    const { notify, user } = useApp();
    const [admins, setAdmins] = useState([]);
    const [envEmails, setEnvEmails] = useState([]);
    const [loading, setLoading] = useState(true);
    const [showModal, setShowModal] = useState(false);
    const [form, setForm] = useState({ email: '', name: '' });

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const r = await api.get('/api/admin/admins');
            setAdmins(r?.admins || []);
            setEnvEmails(r?.env_admin_emails || []);
        } catch (e) { notify.error('Erreur chargement'); }
        finally { setLoading(false); }
    }, [notify]);

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

    const promote = async () => {
        if (!form.email.trim()) return notify.error('Email requis');
        const r = await api.post('/api/admin/admins', form);
        if (r?.error) return notify.error(r.error);
        notify.success('Promu admin');
        setShowModal(false);
        setForm({ email: '', name: '' });
        load();
    };

    const demote = async (a) => {
        if (!confirm(`Retirer le rôle admin de ${a.email} ?`)) return;
        const r = await api.del(`/api/admin/admins/${a.id}`);
        if (r?.error) return notify.error(r.error);
        notify.success('Rôle retiré');
        load();
    };

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Administrateurs</h1>
                    <p className="page-subtitle">Comptes ayant accès à l'admin et aux endpoints sensibles.</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-primary btn-sm" onClick={() => setShowModal(true)}>
                        <Icons.Plus className="w-4 h-4" /> Promouvoir
                    </button>
                </div>
            </div>
            <div className="page-content">
                {envEmails.length > 0 && (
                    <div style={{ background: '#eff6ff', border: '1px solid #bfdbfe', padding: '0.75rem 1rem', borderRadius: 8, marginBottom: '1rem' }}>
                        <div style={{ fontWeight: 600, marginBottom: 4 }}>🔒 Admins via env <code>ADMIN_EMAILS</code> (toujours actifs)</div>
                        <div style={{ fontSize: '0.85rem', color: '#475569' }}>{envEmails.join(', ')}</div>
                    </div>
                )}

                {loading ? <SkeletonCards count={3} columns={1} /> : admins.length === 0 ? (
                    <div className="empty-state">
                        <Icons.Shield className="empty-state-icon" />
                        <h3>Aucun admin DB</h3>
                        <p>Cliquez sur "Promouvoir" pour ajouter un admin.</p>
                    </div>
                ) : (
                    <div className="data-table-container">
                        <table className="data-table">
                            <thead><tr>
                                <th>Email</th>
                                <th>Nom</th>
                                <th>Source</th>
                                <th>Dernière connexion</th>
                                <th>Actions</th>
                            </tr></thead>
                            <tbody>
                                {admins.map(a => {
                                    const fromEnv = envEmails.includes((a.email || '').toLowerCase());
                                    return (
                                        <tr key={a.id}>
                                            <td><strong>{a.email}</strong>{a.id === user?.id && <span style={{ marginLeft: 6, fontSize: '0.7rem', background: '#dbeafe', color: '#1e40af', padding: '0.1rem 0.4rem', borderRadius: 4 }}>vous</span>}</td>
                                            <td>{a.name || '-'}</td>
                                            <td>{fromEnv ? <span style={{ color: '#0369a1' }}>env</span> : <span style={{ color: '#16a34a' }}>db</span>}</td>
                                            <td>{a.last_login ? new Date(a.last_login).toLocaleString('fr-CH') : '-'}</td>
                                            <td>
                                                {a.id !== user?.id && !fromEnv && (
                                                    <button className="btn btn-secondary btn-sm" onClick={() => demote(a)} style={{ color: '#dc2626' }}>Retirer</button>
                                                )}
                                            </td>
                                        </tr>
                                    );
                                })}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 460 }}>
                        <div className="modal-header">
                            <h2>Promouvoir un utilisateur admin</h2>
                            <button className="modal-close" onClick={() => setShowModal(false)}>×</button>
                        </div>
                        <div className="modal-body">
                            <p style={{ color: '#64748b', fontSize: '0.85rem', marginBottom: '1rem' }}>
                                L'utilisateur recevra le rôle "admin". S'il n'existe pas encore, son compte sera créé.
                            </p>
                            <div className="form-group">
                                <label>Email *</label>
                                <input className="form-input" type="email" value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} placeholder="user@example.com" />
                            </div>
                            <div className="form-group">
                                <label>Nom (optionnel)</label>
                                <input className="form-input" value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} />
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={promote}>Promouvoir</button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== SUPPORT ====================
// ==================== PARTNERS VIEW ====================
const PartnersView = () => {
    const { notify } = useApp();
    const [partners, setPartners] = useState([]);
    const [loading, setLoading] = useState(true);
    const [showModal, setShowModal] = useState(false);
    const [editing, setEditing] = useState(null);
    const [form, setForm] = useState({ name: '', logo_url: '', website_url: '', description: '', sort_order: 0 });

    const loadPartners = useCallback(async () => {
        setLoading(true);
        const data = await api.get('/api/admin/partners');
        if (Array.isArray(data)) setPartners(data);
        setLoading(false);
    }, []);

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

    const openNew = () => {
        setEditing(null);
        setForm({ name: '', logo_url: '', website_url: '', description: '', sort_order: 0 });
        setShowModal(true);
    };
    const openEdit = (p) => {
        setEditing(p);
        setForm({ name: p.name, logo_url: p.logo_url || '', website_url: p.website_url || '', description: p.description || '', sort_order: p.sort_order || 0 });
        setShowModal(true);
    };

    const handleSave = async () => {
        if (!form.name.trim()) { notify.error('Nom requis'); return; }
        if (editing) {
            await api.put(`/api/admin/partners/${editing.id}`, form);
            notify.success('Partenaire mis a jour');
        } else {
            await api.post('/api/admin/partners', form);
            notify.success('Partenaire ajoute');
        }
        setShowModal(false);
        loadPartners();
    };

    const handleToggle = async (p) => {
        await api.put(`/api/admin/partners/${p.id}`, { is_active: p.is_active ? 0 : 1 });
        notify.info(p.is_active ? 'Partenaire desactive' : 'Partenaire active');
        loadPartners();
    };

    const handleDelete = async (p) => {
        if (!confirm(`Supprimer ${p.name} ?`)) return;
        await api.del(`/api/admin/partners/${p.id}`);
        notify.success('Partenaire supprime');
        loadPartners();
    };

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Partenaires</h1>
                    <p className="page-subtitle">Logos et liens affich&eacute;s sur helvia.app</p>
                </div>
                <button className="btn btn-primary" onClick={openNew}><Icons.Plus className="w-4 h-4" /> Ajouter</button>
            </div>
            <div className="page-content">
                {loading ? (
                    <SkeletonCards count={3} columns={1} />
                ) : partners.length === 0 ? (
                    <div className="empty-state">
                        <Icons.Link className="empty-state-icon" />
                        <h3>Aucun partenaire</h3>
                        <p>Ajoutez des partenaires pour les afficher sur le site.</p>
                    </div>
                ) : (
                    <div className="data-table-container">
                        <table className="data-table">
                            <thead><tr>
                                <th>Logo</th>
                                <th>Nom</th>
                                <th>Site web</th>
                                <th>Ordre</th>
                                <th>Statut</th>
                                <th>Actions</th>
                            </tr></thead>
                            <tbody>
                                {partners.map(p => (
                                    <tr key={p.id}>
                                        <td>
                                            {p.logo_url ? (
                                                <img src={p.logo_url} alt={p.name} style={{ maxHeight: 28, maxWidth: 100, objectFit: 'contain' }} />
                                            ) : (
                                                <span style={{ color: 'var(--text-muted)', fontSize: '0.8rem' }}>Pas de logo</span>
                                            )}
                                        </td>
                                        <td style={{ fontWeight: 600 }}>{p.name}</td>
                                        <td>
                                            {p.website_url ? (
                                                <a href={p.website_url} target="_blank" rel="noopener noreferrer" style={{ color: 'var(--primary)', textDecoration: 'none', fontSize: '0.85rem' }}>
                                                    {p.website_url.replace(/^https?:\/\//, '').replace(/\/$/, '')}
                                                </a>
                                            ) : '-'}
                                        </td>
                                        <td>{p.sort_order}</td>
                                        <td>
                                            <span className={`badge ${p.is_active ? 'badge-success' : 'badge-gray'}`}>
                                                {p.is_active ? 'Actif' : 'Inactif'}
                                            </span>
                                        </td>
                                        <td>
                                            <div style={{ display: 'flex', gap: '0.35rem' }}>
                                                <button className="btn-icon" title="Activer/Desactiver" onClick={() => handleToggle(p)}>
                                                    {p.is_active ? (
                                                        <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" /></svg>
                                                    ) : (
                                                        <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>
                                                    )}
                                                </button>
                                                <button className="btn-icon" title="Modifier" onClick={() => openEdit(p)}><Icons.Edit className="w-4 h-4" /></button>
                                                <button className="btn-icon btn-icon-danger" title="Supprimer" onClick={() => handleDelete(p)}><Icons.Trash className="w-4 h-4" /></button>
                                            </div>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal-content" onClick={e => e.stopPropagation()}>
                        <div className="modal-header">
                            <h2>{editing ? 'Modifier le partenaire' : 'Nouveau partenaire'}</h2>
                            <button className="modal-close" onClick={() => setShowModal(false)}><Icons.X className="w-5 h-5" /></button>
                        </div>
                        <div className="modal-body">
                            <div className="form-group">
                                <label>Nom *</label>
                                <input type="text" value={form.name} onChange={e => setForm({...form, name: e.target.value})} placeholder="Nom du partenaire" />
                            </div>
                            <div className="form-group">
                                <label>URL du logo</label>
                                <input type="url" value={form.logo_url} onChange={e => setForm({...form, logo_url: e.target.value})} placeholder="https://example.com/logo.svg" />
                                {form.logo_url && (
                                    <div style={{ marginTop: '0.5rem', padding: '0.75rem', background: 'var(--bg-light)', borderRadius: '8px', textAlign: 'center' }}>
                                        <img src={form.logo_url} alt="Preview" style={{ maxHeight: 40, maxWidth: 160, objectFit: 'contain' }} onError={e => e.target.style.display = 'none'} />
                                    </div>
                                )}
                            </div>
                            <div className="form-group">
                                <label>Site web</label>
                                <input type="url" value={form.website_url} onChange={e => setForm({...form, website_url: e.target.value})} placeholder="https://example.com" />
                            </div>
                            <div className="form-group">
                                <label>Description</label>
                                <input type="text" value={form.description} onChange={e => setForm({...form, description: e.target.value})} placeholder="Courte description" />
                            </div>
                            <div className="form-group">
                                <label>Ordre d'affichage</label>
                                <input type="number" value={form.sort_order} onChange={e => setForm({...form, sort_order: parseInt(e.target.value) || 0})} />
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={handleSave}>{editing ? 'Enregistrer' : 'Ajouter'}</button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== LINE DETAIL VIEW ====================
const LineDetailView = () => {
    const { selectedLine, setSelectedLine, navigate, notify } = useApp();
    const [tab, setTab] = useState('summary');
    const [clientsList, setClientsList] = useState([]);
    const [twilioAccountsList, setTwilioAccountsList] = useState([]);
    const [calls, setCalls] = useState([]);
    const [loadingCalls, setLoadingCalls] = useState(false);
    const [copied, setCopied] = useState(false);
    const [widgetKey, setWidgetKey] = useState(null);
    const [generatingKey, setGeneratingKey] = useState(false);
    const [origins, setOrigins] = useState([]);
    const [holdMusicUrl, setHoldMusicUrl] = useState(selectedLine?.hold_music_url || '');
    const [uploadingHoldMusic, setUploadingHoldMusic] = useState(false);
    const [musicLibrary, setMusicLibrary] = useState([]);
    const [apiKeys, setApiKeys] = useState([]);
    const [loadingApiKeys, setLoadingApiKeys] = useState(false);
    const [revealedKeys, setRevealedKeys] = useState({});
    const [showNewKeyForm, setShowNewKeyForm] = useState(false);
    const [newKeyLabel, setNewKeyLabel] = useState('');
    const [creatingKey, setCreatingKey] = useState(false);
    const [newlyCreatedKey, setNewlyCreatedKey] = useState(null);
    const [sendingNotify, setSendingNotify] = useState(false);
    const [waActivating, setWaActivating] = useState(false);

    const activateWhatsApp = async () => {
        if (!selectedLine?.id) return;
        if (!selectedLine?.client_id) { notify.error('Cette ligne n\'est attribuée à aucun client.'); return; }
        if (!confirm(`Activer WhatsApp Business sur ${selectedLine.phone_number} ?\n\nVocal détectera le sender Twilio existant pour ce numéro, synchronisera les webhooks et activera le bot IA. Le client recevra un email de confirmation.`)) return;
        setWaActivating(true);
        try {
            const r = await api.post(`/api/admin/lines/${selectedLine.id}/whatsapp-activate`, {});
            if (r?.error) { notify.error(r.error + (r.detail ? ' - ' + JSON.stringify(r.detail).slice(0, 200) : '')); return; }
            if (r?.ready === false) { notify.error(r.message || 'Sender Twilio introuvable'); return; }
            notify.success(r?.message || 'WhatsApp activé');
            // Rafraîchit la ligne pour màj wa_bot_enabled
            const updated = await api.get(`/api/lines/${selectedLine.id}`);
            if (updated?.line) setSelectedLine(updated.line);
        } catch (e) { notify.error('Erreur : ' + e.message); }
        finally { setWaActivating(false); }
    };

    const deactivateWhatsApp = async () => {
        if (!selectedLine?.id) return;
        if (!confirm(`Désactiver le bot WhatsApp sur ${selectedLine.phone_number} ?\n\nLes messages WhatsApp ne seront plus routés vers le bot IA (le sender Twilio reste actif).`)) return;
        setWaActivating(true);
        try {
            const r = await api.post(`/api/admin/lines/${selectedLine.id}/whatsapp-deactivate`, {});
            if (r?.error) { notify.error(r.error); return; }
            notify.success('Bot WhatsApp désactivé');
            const updated = await api.get(`/api/lines/${selectedLine.id}`);
            if (updated?.line) setSelectedLine(updated.line);
        } catch (e) { notify.error('Erreur : ' + e.message); }
        finally { setWaActivating(false); }
    };

    const sendWhatsAppActivationNotification = async (force = false) => {
        if (!selectedLine?.id) return;
        if (!selectedLine?.wa_bot_enabled) { notify.error('WhatsApp n\'est pas activé sur cette ligne.'); return; }
        const msg = force
            ? `Renvoyer l'email "WhatsApp Business activé" à ${selectedLine.client_name || 'ce client'} ?\n\nL'email a déjà été envoyé. Cliquez OK pour le renvoyer quand même.`
            : `Envoyer l'email "WhatsApp Business activé" à ${selectedLine.client_name || 'ce client'} ?\n\nUne copie sera envoyée à info@helvia.app.`;
        if (!confirm(msg)) return;
        setSendingNotify(true);
        try {
            const r = await api.post(`/api/admin/lines/${selectedLine.id}/whatsapp-notify`, { force });
            if (r?.error) notify.error('Erreur : ' + r.error);
            else if (r?.result?.skipped === 'already_notified') notify.error('Déjà notifié. Cliquez "Forcer" pour renvoyer.');
            else if (r?.result?.skipped === 'not_online') notify.error(`Sender pas encore ONLINE chez Twilio (statut: ${r.result.status || 'inconnu'}).`);
            else if (r?.result?.skipped) notify.error('Pas envoyé : ' + r.result.skipped);
            else if (r?.result?.sent) notify.success('Email envoyé à ' + r.result.email);
        } catch (e) { notify.error('Erreur : ' + e.message); }
        finally { setSendingNotify(false); }
    };

    const sendAssignmentNotification = async (force = false) => {
        if (!selectedLine?.id) return;
        if (!selectedLine?.client_id) {
            notify.error('Cette ligne n\'est pas attribuée à un client.');
            return;
        }
        const msg = force
            ? `Renvoyer l'email "Nouvelle ligne ajoutée" à ${selectedLine.client_name || 'ce client'} ?\n\nL'email a déjà été envoyé. Cliquez OK pour le renvoyer quand même.`
            : `Envoyer l'email "Nouvelle ligne ajoutée" à ${selectedLine.client_name || 'ce client'} ?\n\nUne copie sera envoyée à info@helvia.app.`;
        if (!confirm(msg)) return;
        setSendingNotify(true);
        try {
            const r = await api.post(`/api/admin/lines/${selectedLine.id}/notify-assignment`, { force });
            if (r?.error) notify.error('Erreur : ' + r.error);
            else if (r?.result?.skipped === 'already_notified') notify.error('Déjà notifié. Cochez "Forcer" pour renvoyer.');
            else if (r?.result?.skipped) notify.error('Pas envoyé : ' + r.result.skipped);
            else if (r?.result?.sent) notify.success('Email envoyé à ' + r.result.email);
        } catch (e) { notify.error('Erreur : ' + e.message); }
        finally { setSendingNotify(false); }
    };

    const loadApiKeys = useCallback(async () => {
        setLoadingApiKeys(true);
        try {
            const keys = await api.get('/api/api-keys');
            if (Array.isArray(keys)) {
                const lineKeys = keys.filter(k => k.line_id === selectedLine?.id || (k.line_ids && k.line_ids.includes(selectedLine?.id)));
                setApiKeys(lineKeys);
            }
        } catch (e) {}
        finally { setLoadingApiKeys(false); }
    }, [selectedLine?.id]);

    const toggleApiKeyStatus = async (key) => {
        try {
            await api.put(`/api/api-keys/${key.id}`, { is_active: key.is_active ? 0 : 1 });
            notify.success(key.is_active ? 'Clé révoquée' : 'Clé réactivée');
            loadApiKeys();
        } catch (e) { notify.error('Erreur'); }
    };

    const deleteApiKeyFromLine = async (id) => {
        if (!confirm('Supprimer définitivement cette clé API ?')) return;
        try {
            await api.del(`/api/api-keys/${id}`);
            notify.success('Clé supprimée');
            loadApiKeys();
        } catch (e) { notify.error('Erreur'); }
    };

    const maskKey = (key) => key ? key.slice(0, 6) + '••••••••••••••••' + key.slice(-4) : '';

    const createApiKeyForLine = async () => {
        setCreatingKey(true);
        try {
            const res = await api.post('/api/api-keys', { label: newKeyLabel || 'API Key', line_id: selectedLine.id, line_ids: [selectedLine.id] });
            if (res?.key_value) {
                setNewlyCreatedKey(res.key_value);
                setNewKeyLabel('');
                notify.success('Clé API créée');
                loadApiKeys();
            } else {
                notify.error(res?.error || 'Erreur lors de la création');
            }
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setCreatingKey(false); }
    };

    const loadOrigins = useCallback(async () => {
        try {
            const data = await api.get('/api/widget-origins');
            if (Array.isArray(data)) setOrigins(data);
        } catch (e) {}
    }, []);

    useEffect(() => {
        Promise.all([api.get('/api/admin/clients'), api.get('/api/twilio-accounts')])
            .then(([clientsData, twilioData]) => {
                setClientsList(clientsData?.clients || []);
                setTwilioAccountsList(Array.isArray(twilioData) ? twilioData : []);
            })
            .catch(() => {});
    }, []);

    useEffect(() => {
        if (selectedLine?.id) {
            setLoadingCalls(true);
            api.get(`/api/calls?line_id=${selectedLine.id}&limit=10`)
                .then(data => setCalls(data.calls || []))
                .catch(() => {})
                .finally(() => setLoadingCalls(false));
            api.get('/api/api-keys').then(keys => {
                if (Array.isArray(keys)) {
                    const existing = keys.find(k => k.line_id === selectedLine.id && k.label === 'Widget' && k.is_active);
                    if (existing) setWidgetKey(existing.key_value);
                }
            }).catch(() => {});
            loadOrigins();
            loadApiKeys();
            api.get('/api/hold-music').then(data => {
                if (Array.isArray(data)) setMusicLibrary(data);
            }).catch(() => {});
        }
    }, [selectedLine?.id, loadOrigins]);

    const updateOriginStatus = async (id, status) => {
        try {
            await api.put(`/api/widget-origins/${id}`, { status });
            notify.success(status === 'approved' ? 'Origine approuvée' : 'Origine refusée');
            loadOrigins();
        } catch (e) { notify.error('Erreur'); }
    };

    const deleteOrigin = async (id) => {
        try {
            await api.del(`/api/widget-origins/${id}`);
            notify.success('Origine supprimée');
            loadOrigins();
        } catch (e) { notify.error('Erreur'); }
    };

    const generateWidgetKey = async () => {
        setGeneratingKey(true);
        try {
            const res = await api.post('/api/api-keys', { label: 'Widget', line_id: l.id });
            if (res?.key_value) {
                setWidgetKey(res.key_value);
                notify.success('Clé Widget générée');
            } else {
                notify.error(res?.error || 'Erreur lors de la création');
            }
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setGeneratingKey(false); }
    };

    if (!selectedLine) return <div className="page-content"><p>Aucune ligne sélectionnée.</p></div>;

    const l = selectedLine;
    const typeLabel = LINE_TYPES[l.type]?.label || l.type;
    const typeColor = LINE_TYPES[l.type]?.color || 'badge-gray';

    const widgetSnippet = widgetKey
        ? `<!-- Vocal Widget - ${l.phone_number} -->
<script src="https://widget.helvia.app/assets/js/vocal-widget.js"><\/script>
<script>
  VocalWidget.init({
    token: '${widgetKey}',
  });
<\/script>`
        : `<!-- Vocal Widget -->
<script src="https://widget.helvia.app/assets/js/vocal-widget.js"><\/script>
<script>
  // Générez une clé API ci-dessous, ou utilisez un token JWT
  VocalWidget.init({
    token: 'VOTRE_TOKEN_ICI',
  });
<\/script>`;

    const copySnippet = () => {
        navigator.clipboard.writeText(widgetSnippet);
        setCopied(true);
        notify.success('Code copié dans le presse-papier');
        setTimeout(() => setCopied(false), 2000);
    };

    return (
        <>
            <div className="page-header">
                <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
                    <button className="btn btn-secondary btn-sm" onClick={() => navigate('lines')} style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
                        ← Retour
                    </button>
                    <div>
                        <h1 className="page-title" style={{ fontFamily: 'ui-monospace, monospace' }}>{l.phone_number}</h1>
                        <p className="page-subtitle">{l.label || l.description || 'Ligne téléphonique'}</p>
                    </div>
                </div>
                <div className="page-actions">
                    <span className={`badge ${l.is_active ? 'badge-success' : 'badge-gray'}`} style={{ fontSize: '0.8rem', padding: '0.3rem 0.75rem' }}>
                        {l.is_active ? 'Active' : 'Inactive'}
                    </span>
                    <span className={`badge ${typeColor}`} style={{ fontSize: '0.8rem', padding: '0.3rem 0.75rem' }}>
                        {typeLabel}
                    </span>
                </div>
            </div>
            <div className="page-content">
                <div className="tabs" style={{ marginBottom: '1.5rem' }}>
                    <button className={`tab ${tab === 'summary' ? 'active' : ''}`} onClick={() => setTab('summary')}>Résumé</button>
                    <button className={`tab ${tab === 'config' ? 'active' : ''}`} onClick={() => setTab('config')}>Configuration</button>
                    <button className={`tab ${tab === 'client' ? 'active' : ''}`} onClick={() => setTab('client')}>Client</button>
                    <button className={`tab ${tab === 'gateway' ? 'active' : ''}`} onClick={() => setTab('gateway')}>Gateway</button>
                    <button className={`tab ${tab === 'settings' ? 'active' : ''}`} onClick={() => setTab('settings')}>Paramètres</button>
                    <button className={`tab ${tab === 'widget' ? 'active' : ''}`} onClick={() => setTab('widget')}>Widget</button>
                    <button className={`tab ${tab === 'api' ? 'active' : ''}`} onClick={() => setTab('api')}>API</button>
                </div>

                {tab === 'summary' && (
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
                        <div className="card" style={{ padding: '1.25rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '1rem', color: 'var(--text-secondary)' }}>Informations</h3>
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                                {[
                                    ['Numéro', l.phone_number],
                                    ['Libellé', l.label || '-'],
                                    ['Type', typeLabel],
                                    ['Client', l.client_name || 'Non assignée'],
                                    ['Compte Twilio', l.twilio_account_name || '-'],
                                    ['Caller ID', l.caller_id_mode === 'line' ? 'Numéro de la ligne' : 'Numéro de l\'appelant'],
                                    ['Créée le', formatDate(l.created_at)],
                                ].map(([label, value]) => (
                                    <div key={label} style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>{label}</span>
                                        <span style={{ fontSize: '0.8rem', fontWeight: 600 }}>{value}</span>
                                    </div>
                                ))}
                            </div>
                            {l.client_id && (
                                <div style={{ marginTop: '1rem', paddingTop: '0.75rem', borderTop: '1px dashed var(--border)', display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                    {/* État WhatsApp + boutons activer/désactiver */}
                                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0.4rem 0' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>WhatsApp</span>
                                        <span className={`badge ${l.wa_bot_enabled ? 'badge-success' : 'badge-gray'}`}>
                                            {l.wa_bot_enabled ? '💬 Actif' : '○ Inactif'}
                                        </span>
                                    </div>
                                    {l.wa_bot_enabled ? (
                                        <>
                                            <button className="btn btn-secondary btn-sm"
                                                disabled={waActivating}
                                                onClick={deactivateWhatsApp}
                                                title="Désactive le bot WhatsApp (le sender Twilio reste actif)">
                                                {waActivating ? '…' : '⏸ Désactiver WhatsApp'}
                                            </button>
                                            <button className="btn btn-secondary btn-sm"
                                                disabled={sendingNotify}
                                                onClick={() => sendWhatsAppActivationNotification(false)}
                                                title="Envoie l'email 'WhatsApp Business activé' au client (BCC info@helvia.app)">
                                                {sendingNotify ? 'Envoi…' : '✉️ Envoyer la notif d\'activation WhatsApp'}
                                            </button>
                                            <button className="btn btn-secondary btn-sm"
                                                disabled={sendingNotify}
                                                onClick={() => sendWhatsAppActivationNotification(true)}
                                                style={{ fontSize: '0.72rem' }}
                                                title="Force le renvoi même si déjà envoyé">
                                                ↻ Forcer le renvoi WA
                                            </button>
                                        </>
                                    ) : (
                                        <button className="btn btn-primary btn-sm"
                                            disabled={waActivating}
                                            onClick={activateWhatsApp}
                                            title="Détecte le sender WhatsApp Twilio existant, l'importe et active le bot IA. Envoie un email au client.">
                                            {waActivating ? 'Activation…' : '💬 Activer WhatsApp'}
                                        </button>
                                    )}

                                    <div style={{ borderTop: '1px dashed var(--border)', margin: '0.4rem 0 0.2rem' }} />

                                    <button className="btn btn-secondary btn-sm"
                                        disabled={sendingNotify}
                                        onClick={() => sendAssignmentNotification(false)}
                                        title="Envoie l'email 'Nouvelle ligne ajoutée' au client (si pas déjà envoyé). BCC : info@helvia.app">
                                        {sendingNotify ? 'Envoi…' : '✉️ Envoyer la notif d\'attribution'}
                                    </button>
                                    <button className="btn btn-secondary btn-sm"
                                        disabled={sendingNotify}
                                        onClick={() => sendAssignmentNotification(true)}
                                        style={{ fontSize: '0.72rem' }}
                                        title="Force le renvoi même si l'email a déjà été envoyé">
                                        ↻ Forcer le renvoi
                                    </button>
                                </div>
                            )}
                        </div>

                        <div className="card" style={{ padding: '1.25rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '1rem', color: 'var(--text-secondary)' }}>Configuration</h3>
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                                {l.type === 'forward' && (
                                    <>
                                        <div style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                            <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Redirection</span>
                                            <span style={{ fontSize: '0.8rem', fontWeight: 600, fontFamily: 'ui-monospace, monospace' }}>{l.forward_number || '-'}</span>
                                        </div>
                                        {[1, 2, 3].map(n => l[`fallback_number_${n}`] ? (
                                            <div key={n} style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                                <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Fallback {n}</span>
                                                <span style={{ fontSize: '0.8rem', fontWeight: 600, fontFamily: 'ui-monospace, monospace' }}>{l[`fallback_number_${n}`]}</span>
                                            </div>
                                        ) : null)}
                                    </>
                                )}
                                {l.webhook_url && (
                                    <div style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Webhook{l.type === 'external_api' ? ' primaire' : ''}</span>
                                        <span style={{ fontSize: '0.8rem', fontWeight: 600, wordBreak: 'break-all', maxWidth: '200px', textAlign: 'right' }}>{l.webhook_url}</span>
                                    </div>
                                )}
                                {l.webhook_url_fallback && (
                                    <div style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Webhook fallback</span>
                                        <span style={{ fontSize: '0.8rem', fontWeight: 600, wordBreak: 'break-all', maxWidth: '200px', textAlign: 'right' }}>{l.webhook_url_fallback}</span>
                                    </div>
                                )}
                                {l.type === 'external_api' && (
                                    <div style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Timeout healthcheck</span>
                                        <span style={{ fontSize: '0.8rem', fontWeight: 600 }}>{l.webhook_timeout_ms || 3000} ms</span>
                                    </div>
                                )}
                                {l.voice_welcome_message && (
                                    <div style={{ padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)', display: 'block', marginBottom: '0.25rem' }}>Message d'accueil</span>
                                        <span style={{ fontSize: '0.8rem', fontStyle: 'italic' }}>"{l.voice_welcome_message}"</span>
                                    </div>
                                )}
                                {l.description && (
                                    <div style={{ padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                        <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)', display: 'block', marginBottom: '0.25rem' }}>Description</span>
                                        <span style={{ fontSize: '0.8rem' }}>{l.description}</span>
                                    </div>
                                )}
                                <div style={{ display: 'flex', justifyContent: 'space-between', padding: '0.4rem 0', borderBottom: '1px solid var(--border)' }}>
                                    <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Total appels</span>
                                    <span style={{ fontSize: '0.8rem', fontWeight: 700 }}>{l.total_calls || 0}</span>
                                </div>
                            </div>
                        </div>

                        <div className="card" style={{ padding: '1.25rem', gridColumn: '1 / -1' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '1rem', color: 'var(--text-secondary)' }}>Derniers appels</h3>
                            {loadingCalls ? (
                                <div style={{ textAlign: 'center', padding: '2rem', color: 'var(--text-muted)' }}>Chargement...</div>
                            ) : calls.length === 0 ? (
                                <div style={{ textAlign: 'center', padding: '2rem', color: 'var(--text-muted)', fontSize: '0.85rem' }}>Aucun appel pour cette ligne.</div>
                            ) : (
                                <div className="table-container">
                                    <table className="data-table" style={{ fontSize: '0.8rem' }}>
                                        <thead>
                                            <tr><th>De</th><th>Statut</th><th>Durée</th><th>Date</th></tr>
                                        </thead>
                                        <tbody>
                                            {calls.map(c => {
                                                const dur = c.duration ? `${Math.floor(c.duration / 60)}:${String(c.duration % 60).padStart(2, '0')}` : '-';
                                                const statusMap = { completed: 'badge-success', 'in-progress': 'badge-info', ringing: 'badge-info', busy: 'badge-warning', 'no-answer': 'badge-warning', failed: 'badge-danger', canceled: 'badge-gray' };
                                                const statusLabel = { completed: 'Terminé', 'in-progress': 'En cours', ringing: 'Sonne', busy: 'Occupé', 'no-answer': 'Sans rép.', failed: 'Échoué', canceled: 'Annulé' };
                                                return (
                                                    <tr key={c.id}>
                                                        <td style={{ fontFamily: 'ui-monospace, monospace' }}>{c.caller_number || '-'}</td>
                                                        <td><span className={`badge ${statusMap[c.status] || 'badge-gray'}`}>{statusLabel[c.status] || c.status}</span></td>
                                                        <td>{dur}</td>
                                                        <td>{formatDate(c.created_at)}</td>
                                                    </tr>
                                                );
                                            })}
                                        </tbody>
                                    </table>
                                </div>
                            )}
                        </div>
                    </div>
                )}

                {tab === 'config' && (
                    <div style={{ maxWidth: '760px' }}>
                        <div className="card" style={{ padding: '1.5rem' }}>
                            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
                                <div>
                                    <h3 style={{ fontSize: '1rem', fontWeight: 700, margin: 0 }}>Configuration de la ligne</h3>
                                    <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: '0.2rem 0 0' }}>
                                        Tous les paramètres de routage, accueil, messagerie, enregistrement et transcription.
                                    </p>
                                </div>
                            </div>
                            <LineConfigForm
                                line={selectedLine}
                                clients={clientsList}
                                twilioAccounts={twilioAccountsList}
                                inline
                                hideFields={['phone_number', 'client_id', 'twilio_account_id']}
                                onSaved={(updated) => {
                                    if (updated && updated.id) setSelectedLine({ ...selectedLine, ...updated });
                                }}
                            />
                        </div>
                    </div>
                )}

                {tab === 'client' && (
                    <div style={{ maxWidth: '560px' }}>
                        <div className="card" style={{ padding: '1.5rem' }}>
                            <div style={{ marginBottom: '1.25rem' }}>
                                <h3 style={{ fontSize: '1rem', fontWeight: 700, margin: 0 }}>Client assigné</h3>
                                <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: '0.2rem 0 0' }}>
                                    Le client qui possède cette ligne et reçoit la facturation associée.
                                </p>
                            </div>
                            <LineFieldUpdate
                                line={selectedLine}
                                field="client_id"
                                label="Client"
                                hint="Les clients apparaissent ici dès leur première connexion. Changer le client peut déclencher un transfert Twilio si le nouveau client a un subaccount."
                                options={clientsList.map(c => ({ value: c.id, label: `${c.name} (${c.email})` }))}
                                onSaved={(updated) => setSelectedLine(updated)}
                            />
                        </div>
                    </div>
                )}

                {tab === 'gateway' && (
                    <div style={{ maxWidth: '560px' }}>
                        <div className="card" style={{ padding: '1.5rem' }}>
                            <div style={{ marginBottom: '1.25rem' }}>
                                <h3 style={{ fontSize: '1rem', fontWeight: 700, margin: 0 }}>Compte Twilio (Gateway)</h3>
                                <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: '0.2rem 0 0' }}>
                                    Compte / subaccount Twilio utilisé pour les appels sortants (API) et la facturation côté opérateur.
                                </p>
                            </div>
                            <LineFieldUpdate
                                line={selectedLine}
                                field="twilio_account_id"
                                label="Compte Twilio"
                                hint="Compte Twilio utilisé pour les appels sortants (API) de cette ligne."
                                options={twilioAccountsList.filter(a => a.is_active).map(a => ({ value: a.id, label: `${a.name} (${a.account_sid})` }))}
                                onSaved={(updated) => setSelectedLine(updated)}
                            />
                            <div style={{ marginTop: '1rem', padding: '0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', fontSize: '0.78rem', color: 'var(--text-muted)' }}>
                                <strong>Numéro physique :</strong>{' '}
                                <code style={{ fontFamily: 'ui-monospace, monospace' }}>{selectedLine.phone_number}</code>
                                {selectedLine.twilio_account_name && (
                                    <> · actuellement sur <strong>{selectedLine.twilio_account_name}</strong></>
                                )}
                            </div>
                        </div>
                    </div>
                )}

                {tab === 'settings' && (
                    <div style={{ maxWidth: '720px' }}>
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '0.75rem' }}>Musique d'attente</h3>
                            <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', lineHeight: 1.6, marginBottom: '1rem' }}>
                                Fichier MP3 joué lorsqu'un appelant est mis en attente via le widget navigateur. Si aucun fichier n'est configuré, une musique par défaut sera utilisée.
                            </p>

                            {holdMusicUrl ? (
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.75rem', background: '#f0fdf4', borderRadius: '0.5rem', border: '1px solid #bbf7d0', marginBottom: '1rem' }}>
                                    <Icons.Check className="w-4 h-4" style={{ color: '#16a34a', flexShrink: 0 }} />
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{ fontWeight: 600, fontSize: '0.82rem', color: '#166534' }}>Musique configurée</div>
                                        <div style={{ fontSize: '0.72rem', color: '#16a34a', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                            {(() => {
                                                const match = musicLibrary.find(m => m.file_url === holdMusicUrl);
                                                return match ? match.name : holdMusicUrl.split('/').pop();
                                            })()}
                                        </div>
                                    </div>
                                    <audio controls src={holdMusicUrl} style={{ height: '32px', maxWidth: '180px' }} />
                                    <button className="btn btn-danger btn-sm" onClick={async () => {
                                        try {
                                            await api.put(`/api/lines/${l.id}`, { hold_music_url: '' });
                                            setHoldMusicUrl('');
                                            setSelectedLine({ ...selectedLine, hold_music_url: '' });
                                            notify.success('Musique d\'attente retirée');
                                        } catch (e) { notify.error('Erreur'); }
                                    }} style={{ padding: '0.25rem 0.5rem', fontSize: '0.7rem' }}>
                                        Retirer
                                    </button>
                                </div>
                            ) : (
                                <div style={{ padding: '1rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px dashed var(--border)', textAlign: 'center', marginBottom: '1rem' }}>
                                    <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', marginBottom: '0' }}>Aucune musique configurée - la musique par défaut sera utilisée.</p>
                                </div>
                            )}

                            {musicLibrary.length > 0 && (
                                <div style={{ marginBottom: '1rem' }}>
                                    <div style={{ fontSize: '0.82rem', fontWeight: 600, color: 'var(--text-muted)', marginBottom: '0.5rem' }}>Choisir dans la bibliothèque</div>
                                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
                                        {musicLibrary.map(m => {
                                            const isActive = holdMusicUrl === m.file_url;
                                            return (
                                                <div key={m.id} style={{
                                                    display: 'flex', alignItems: 'center', gap: '0.6rem', padding: '0.5rem 0.75rem',
                                                    background: isActive ? '#f0fdf4' : 'var(--bg-secondary)',
                                                    borderRadius: '0.5rem', border: `1px solid ${isActive ? '#bbf7d0' : 'var(--border)'}`,
                                                    cursor: 'pointer', transition: 'all 0.15s'
                                                }} onClick={async () => {
                                                    if (isActive) return;
                                                    try {
                                                        await api.put(`/api/lines/${l.id}`, { hold_music_url: m.file_url });
                                                        setHoldMusicUrl(m.file_url);
                                                        setSelectedLine({ ...selectedLine, hold_music_url: m.file_url });
                                                        notify.success(`Musique "${m.name}" activée`);
                                                    } catch (e) { notify.error('Erreur'); }
                                                }}>
                                                    <div style={{
                                                        width: '18px', height: '18px', borderRadius: '50%',
                                                        border: `2px solid ${isActive ? '#16a34a' : '#cbd5e1'}`,
                                                        background: isActive ? '#16a34a' : 'transparent',
                                                        display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0
                                                    }}>
                                                        {isActive && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3"><polyline points="20 6 9 17 4 12"/></svg>}
                                                    </div>
                                                    <div style={{ flex: 1, minWidth: 0 }}>
                                                        <div style={{ fontWeight: 600, fontSize: '0.82rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{m.name}</div>
                                                    </div>
                                                    {isActive && <span style={{ fontSize: '0.7rem', fontWeight: 600, color: '#16a34a' }}>Actif</span>}
                                                </div>
                                            );
                                        })}
                                    </div>
                                </div>
                            )}

                            {musicLibrary.length === 0 && (
                                <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', fontStyle: 'italic', margin: '0.5rem 0' }}>
                                    Aucune musique dans la bibliothèque. Ajoutez-en via Paramètres &gt; Musique d'attente.
                                </p>
                            )}

                            <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', marginTop: '0.5rem' }}>
                                <label className="btn btn-sm" style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.35rem', fontSize: '0.78rem' }}>
                                    <Icons.Plus className="w-3 h-3" />
                                    {uploadingHoldMusic ? 'Upload...' : 'Uploader un fichier personnalisé'}
                                    <input type="file" accept="audio/mpeg,audio/mp3" style={{ display: 'none' }} disabled={uploadingHoldMusic} onChange={async (e) => {
                                        const file = e.target.files?.[0];
                                        if (!file) return;
                                        if (file.size > 10 * 1024 * 1024) { notify.error('Fichier trop volumineux (max 10 Mo)'); return; }
                                        setUploadingHoldMusic(true);
                                        try {
                                            const formData = new FormData();
                                            formData.append('file', file);
                                            formData.append('type', 'hold_music');
                                            const resp = await fetch(CONFIG.API_URL + '/api/audio/upload', {
                                                method: 'POST',
                                                headers: { 'Authorization': 'Bearer ' + api.token },
                                                body: formData,
                                            });
                                            const data = await resp.json();
                                            if (data.url) {
                                                await api.put(`/api/lines/${l.id}`, { hold_music_url: data.url });
                                                setHoldMusicUrl(data.url);
                                                setSelectedLine({ ...selectedLine, hold_music_url: data.url });
                                                notify.success('Musique d\'attente enregistrée');
                                            } else {
                                                notify.error(data.error || 'Erreur upload');
                                            }
                                        } catch (e) { notify.error('Erreur: ' + e.message); }
                                        finally { setUploadingHoldMusic(false); e.target.value = ''; }
                                    }} />
                                </label>
                            </div>
                        </div>
                    </div>
                )}

                {tab === 'widget' && (
                    <div style={{ maxWidth: '720px' }}>
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'flex-start', gap: '1rem', marginBottom: '1.25rem' }}>
                                <div style={{ width: '48px', height: '48px', borderRadius: '12px', background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                                    <Icons.Phone className="w-6 h-6" style={{ color: '#fff' }} />
                                </div>
                                <div>
                                    <h3 style={{ fontSize: '1.1rem', fontWeight: 700, marginBottom: '0.25rem' }}>Widget Vocal</h3>
                                    <p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', lineHeight: 1.5 }}>
                                        Intégrez un widget de réception d'appels sur n'importe quel site web. 
                                        Les appels entrants sur <strong>{l.phone_number}</strong> pourront être gérés directement depuis le navigateur.
                                    </p>
                                </div>
                            </div>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '0.75rem' }}>
                                {[
                                    ['Répondre / Refuser', 'Gérez les appels entrants'],
                                    ['Mise en attente', 'Avec musique d\'attente'],
                                    ['Historique', 'Consultez les derniers appels'],
                                ].map(([title, desc]) => (
                                    <div key={title} style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                                        <div style={{ fontSize: '0.8rem', fontWeight: 600, marginBottom: '0.15rem' }}>{title}</div>
                                        <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>{desc}</div>
                                    </div>
                                ))}
                            </div>
                        </div>

                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700 }}>Code d'intégration</h3>
                                <div style={{ display: 'flex', gap: '0.5rem' }}>
                                    {!widgetKey && (
                                        <button className="btn btn-secondary btn-sm" onClick={generateWidgetKey} disabled={generatingKey} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                            <Icons.Plus className="w-4 h-4" />
                                            {generatingKey ? 'Génération...' : 'Générer une clé'}
                                        </button>
                                    )}
                                    <button className="btn btn-primary btn-sm" onClick={copySnippet} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                        {copied ? <><Icons.Check className="w-4 h-4" /> Copié !</> : <><Icons.Copy className="w-4 h-4" /> Copier le code</>}
                                    </button>
                                </div>
                            </div>
                            {widgetKey && (
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.75rem', padding: '0.6rem 0.75rem', background: '#f0fdf4', border: '1px solid #bbf7d0', borderRadius: '0.5rem' }}>
                                    <Icons.Check className="w-4 h-4" style={{ color: '#16a34a', flexShrink: 0 }} />
                                    <span style={{ fontSize: '0.8rem', color: '#166534', fontWeight: 500 }}>Clé Widget active :</span>
                                    <code style={{ fontSize: '0.75rem', fontFamily: 'ui-monospace, monospace', color: '#166534', background: '#dcfce7', padding: '0.15rem 0.5rem', borderRadius: '4px', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{widgetKey}</code>
                                </div>
                            )}
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '0.75rem' }}>
                                Copiez ce code et collez-le avant la balise <code style={{ background: 'var(--bg-secondary)', padding: '0.1rem 0.4rem', borderRadius: '4px', fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem' }}>&lt;/body&gt;</code> de votre site.
                            </p>
                            <pre style={{ background: '#1e293b', color: '#e2e8f0', padding: '1rem', borderRadius: '0.5rem', fontSize: '0.78rem', lineHeight: 1.7, overflowX: 'auto', whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace', border: '1px solid #334155' }}>{widgetSnippet}</pre>
                        </div>

                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <h3 style={{ fontSize: '0.95rem', fontWeight: 700, marginBottom: '0.75rem' }}>Authentification</h3>
                            <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', lineHeight: 1.6, marginBottom: '1rem' }}>
                                Le widget nécessite un <strong>token JWT</strong> valide pour se connecter. Pour l'obtenir via l'API :
                            </p>
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                                    <div style={{ fontSize: '0.7rem', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: '0.4rem' }}>Étape 1 - Connexion</div>
                                    <pre style={{ margin: 0, fontSize: '0.75rem', fontFamily: 'ui-monospace, monospace', whiteSpace: 'pre-wrap', color: 'var(--text-primary)' }}>{`POST https://api.helvia.app/auth/login
{ "email": "user@example.com", "password": "..." }`}</pre>
                                </div>
                                <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '0.75rem', border: '1px solid var(--border)' }}>
                                    <div style={{ fontSize: '0.7rem', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: '0.4rem' }}>Étape 2 - Utilisation</div>
                                    <pre style={{ margin: 0, fontSize: '0.75rem', fontFamily: 'ui-monospace, monospace', whiteSpace: 'pre-wrap', color: 'var(--text-primary)' }}>{`VocalWidget.init({ token: response.token });`}</pre>
                                </div>
                            </div>
                        </div>

                        <div className="card" style={{ padding: '1.5rem', background: 'var(--bg-secondary)', border: '1px dashed var(--border)' }}>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.5rem' }}>
                                <Icons.ExternalLink className="w-4 h-4" style={{ color: 'var(--primary)' }} />
                                <span style={{ fontSize: '0.85rem', fontWeight: 600 }}>Tester le widget</span>
                            </div>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '0.75rem' }}>
                                Testez le widget directement sur notre page de démonstration.
                            </p>
                            <a href="https://widget.helvia.app" target="_blank" rel="noopener noreferrer" className="btn btn-primary btn-sm" style={{ display: 'inline-flex', alignItems: 'center', gap: '0.35rem', textDecoration: 'none' }}>
                                Ouvrir widget.helvia.app <Icons.ExternalLink className="w-3.5 h-3.5" />
                            </a>
                        </div>
                    </div>
                )}

                {tab === 'api' && (
                    <div style={{ maxWidth: '720px' }}>
                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700 }}>Clés API</h3>
                                <div style={{ display: 'flex', gap: '0.5rem' }}>
                                    <button className="btn btn-secondary btn-sm" onClick={loadApiKeys} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                        <Icons.Refresh className="w-3.5 h-3.5" /> Actualiser
                                    </button>
                                    <button className="btn btn-primary btn-sm" onClick={() => { setShowNewKeyForm(true); setNewlyCreatedKey(null); }} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                        <Icons.Plus className="w-3.5 h-3.5" /> Nouvelle clé
                                    </button>
                                </div>
                            </div>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '1rem', lineHeight: 1.5 }}>
                                Clés API associées à cette ligne. Elles permettent l'authentification du widget et des appels via l'API.
                            </p>

                            {showNewKeyForm && (
                                <div style={{ padding: '1rem', background: '#f0f4ff', border: '1px solid #c7d2fe', borderRadius: '0.5rem', marginBottom: '1rem' }}>
                                    {newlyCreatedKey ? (
                                        <div>
                                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.75rem' }}>
                                                <Icons.Check className="w-4 h-4" style={{ color: '#16a34a' }} />
                                                <span style={{ fontSize: '0.85rem', fontWeight: 600, color: '#166534' }}>Clé créée avec succès</span>
                                            </div>
                                            <p style={{ fontSize: '0.78rem', color: '#64748b', marginBottom: '0.5rem' }}>
                                                Copiez cette clé maintenant, elle ne sera plus affichée en clair.
                                            </p>
                                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                                <code style={{
                                                    flex: 1, fontSize: '0.78rem', fontFamily: 'ui-monospace, monospace',
                                                    padding: '0.5rem 0.75rem', background: '#fff', borderRadius: '0.375rem',
                                                    border: '1px solid #e2e8f0', wordBreak: 'break-all'
                                                }}>{newlyCreatedKey}</code>
                                                <button className="btn btn-primary btn-sm" onClick={() => { navigator.clipboard.writeText(newlyCreatedKey); notify.success('Clé copiée'); }} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem', flexShrink: 0 }}>
                                                    <Icons.Copy className="w-3.5 h-3.5" /> Copier
                                                </button>
                                            </div>
                                            <button className="btn btn-secondary btn-sm" onClick={() => { setShowNewKeyForm(false); setNewlyCreatedKey(null); }} style={{ marginTop: '0.75rem', fontSize: '0.75rem' }}>
                                                Fermer
                                            </button>
                                        </div>
                                    ) : (
                                        <div>
                                            <div style={{ fontSize: '0.85rem', fontWeight: 600, marginBottom: '0.5rem' }}>Créer une clé API</div>
                                            <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                                                <input
                                                    type="text"
                                                    className="form-input"
                                                    placeholder="Label (ex: Widget site, App mobile...)"
                                                    value={newKeyLabel}
                                                    onChange={e => setNewKeyLabel(e.target.value)}
                                                    onKeyDown={e => { if (e.key === 'Enter') createApiKeyForLine(); }}
                                                    style={{ flex: 1, fontSize: '0.82rem', padding: '0.45rem 0.75rem' }}
                                                />
                                                <button className="btn btn-primary btn-sm" onClick={createApiKeyForLine} disabled={creatingKey} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem', flexShrink: 0 }}>
                                                    {creatingKey ? 'Création...' : 'Générer'}
                                                </button>
                                                <button className="btn btn-secondary btn-sm" onClick={() => { setShowNewKeyForm(false); setNewKeyLabel(''); }} style={{ flexShrink: 0 }}>
                                                    Annuler
                                                </button>
                                            </div>
                                        </div>
                                    )}
                                </div>
                            )}

                            {loadingApiKeys ? (
                                <div style={{ textAlign: 'center', padding: '2rem', color: 'var(--text-muted)' }}>Chargement...</div>
                            ) : apiKeys.length === 0 && !showNewKeyForm ? (
                                <div style={{ textAlign: 'center', padding: '1.5rem', color: 'var(--text-muted)', fontSize: '0.82rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px solid var(--border)' }}>
                                    Aucune clé API pour cette ligne.
                                </div>
                            ) : (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                    {apiKeys.map(k => (
                                        <div key={k.id} style={{
                                            display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.75rem',
                                            background: 'var(--bg-secondary)', borderRadius: '0.5rem',
                                            border: `1px solid ${k.is_active ? 'var(--border)' : '#fecaca'}`
                                        }}>
                                            <div style={{ flex: 1, minWidth: 0 }}>
                                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.3rem' }}>
                                                    <span style={{ fontSize: '0.82rem', fontWeight: 600 }}>{k.label || 'Sans nom'}</span>
                                                    <span className={`badge ${k.is_active ? 'badge-success' : 'badge-danger'}`} style={{ fontSize: '0.65rem' }}>
                                                        {k.is_active ? 'Active' : 'Révoquée'}
                                                    </span>
                                                </div>
                                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                                    <code style={{
                                                        fontSize: '0.72rem', fontFamily: 'ui-monospace, monospace',
                                                        color: 'var(--text-muted)', background: 'var(--bg-white)',
                                                        padding: '0.15rem 0.5rem', borderRadius: '4px',
                                                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'
                                                    }}>
                                                        {revealedKeys[k.id] ? k.key_value : maskKey(k.key_value)}
                                                    </code>
                                                    <button onClick={() => setRevealedKeys(prev => ({ ...prev, [k.id]: !prev[k.id] }))} style={{
                                                        background: 'none', border: 'none', cursor: 'pointer', padding: '2px',
                                                        color: 'var(--text-muted)', fontSize: '0.7rem'
                                                    }} title={revealedKeys[k.id] ? 'Masquer' : 'Révéler'}>
                                                        {revealedKeys[k.id] ? <Icons.EyeOff className="w-3.5 h-3.5" /> : <Icons.Eye className="w-3.5 h-3.5" />}
                                                    </button>
                                                    <button onClick={() => { navigator.clipboard.writeText(k.key_value); notify.success('Clé copiée'); }} style={{
                                                        background: 'none', border: 'none', cursor: 'pointer', padding: '2px',
                                                        color: 'var(--text-muted)'
                                                    }} title="Copier">
                                                        <Icons.Copy className="w-3.5 h-3.5" />
                                                    </button>
                                                </div>
                                                <div style={{ fontSize: '0.68rem', color: 'var(--text-muted)', marginTop: '0.25rem' }}>
                                                    {k.client_name ? `Client: ${k.client_name}` : ''}
                                                    {k.client_name && k.last_used_at ? ' · ' : ''}
                                                    {k.last_used_at ? `Dernier usage: ${formatDate(k.last_used_at)}` : ''}
                                                    {(k.client_name || k.last_used_at) && k.calls_count ? ' · ' : ''}
                                                    {k.calls_count ? `${k.calls_count} appel${k.calls_count > 1 ? 's' : ''}` : ''}
                                                </div>
                                            </div>
                                            <div style={{ display: 'flex', gap: '0.25rem', flexShrink: 0 }}>
                                                <button
                                                    className={`btn btn-sm ${k.is_active ? 'btn-secondary' : 'btn-primary'}`}
                                                    onClick={() => toggleApiKeyStatus(k)}
                                                    style={{ padding: '0.25rem 0.6rem', fontSize: '0.7rem' }}
                                                >
                                                    {k.is_active ? 'Révoquer' : 'Réactiver'}
                                                </button>
                                                <button className="btn btn-danger btn-icon btn-sm" onClick={() => deleteApiKeyFromLine(k.id)} style={{ padding: '0.25rem 0.4rem' }}>
                                                    <Icons.Trash className="w-3.5 h-3.5" />
                                                </button>
                                            </div>
                                        </div>
                                    ))}
                                </div>
                            )}
                        </div>

                        <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.75rem' }}>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700 }}>Origines autorisées</h3>
                                <button className="btn btn-secondary btn-sm" onClick={loadOrigins} style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}>
                                    <Icons.Refresh className="w-3.5 h-3.5" /> Actualiser
                                </button>
                            </div>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '1rem', lineHeight: 1.5 }}>
                                Les domaines ci-dessous ont tenté d'utiliser le widget. Approuvez ceux que vous autorisez.
                            </p>
                            {origins.length === 0 ? (
                                <div style={{ textAlign: 'center', padding: '1.5rem', color: 'var(--text-muted)', fontSize: '0.82rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: '1px solid var(--border)' }}>
                                    Aucune origine détectée. Les domaines apparaîtront ici dès qu'un site tentera d'utiliser le widget.
                                </div>
                            ) : (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                    {origins.map(o => (
                                        <div key={o.id} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.6rem 0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem', border: `1px solid ${o.status === 'approved' ? '#bbf7d0' : o.status === 'rejected' ? '#fecaca' : '#fed7aa'}` }}>
                                            <code style={{ flex: 1, fontSize: '0.8rem', fontFamily: 'ui-monospace, monospace', fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{o.origin}</code>
                                            <span className={`badge ${o.status === 'approved' ? 'badge-success' : o.status === 'rejected' ? 'badge-danger' : 'badge-warning'}`} style={{ fontSize: '0.7rem', flexShrink: 0 }}>
                                                {o.status === 'approved' ? 'Approuvé' : o.status === 'rejected' ? 'Refusé' : 'En attente'}
                                            </span>
                                            <div style={{ display: 'flex', gap: '0.25rem', flexShrink: 0 }}>
                                                {o.status !== 'approved' && (
                                                    <button className="btn btn-primary btn-sm" onClick={() => updateOriginStatus(o.id, 'approved')} style={{ padding: '0.2rem 0.6rem', fontSize: '0.7rem' }}>
                                                        Approuver
                                                    </button>
                                                )}
                                                {o.status !== 'rejected' && o.status !== 'approved' && (
                                                    <button className="btn btn-secondary btn-sm" onClick={() => updateOriginStatus(o.id, 'rejected')} style={{ padding: '0.2rem 0.6rem', fontSize: '0.7rem' }}>
                                                        Refuser
                                                    </button>
                                                )}
                                                {o.status === 'approved' && (
                                                    <button className="btn btn-secondary btn-sm" onClick={() => updateOriginStatus(o.id, 'rejected')} style={{ padding: '0.2rem 0.6rem', fontSize: '0.7rem' }}>
                                                        Révoquer
                                                    </button>
                                                )}
                                                <button className="btn btn-danger btn-icon btn-sm" onClick={() => deleteOrigin(o.id)} style={{ padding: '0.2rem 0.4rem' }}>
                                                    <Icons.Trash className="w-3.5 h-3.5" />
                                                </button>
                                            </div>
                                        </div>
                                    ))}
                                </div>
                            )}
                        </div>
                    </div>
                )}
            </div>
        </>
    );
};

// ==================== API VIEW ====================
const ApiView = () => {
    const { notify } = useApp();
    const [keys, setKeys] = useState([]);
    const [lines, setLines] = useState([]);
    const [calls, setCalls] = useState([]);
    const [loading, setLoading] = useState(true);
    const [showModal, setShowModal] = useState(false);
    const [editing, setEditing] = useState(null);
    const [form, setForm] = useState({ label: '', line_id: '' });
    const [showSecret, setShowSecret] = useState({});
    const [tab, setTab] = useState('keys');
    const [callsPage, setCallsPage] = useState(1);
    const [callsTotal, setCallsTotal] = useState(0);
    const [testPhone, setTestPhone] = useState('');
    const [testVoice, setTestVoice] = useState('Polly.Lea');
    const [testMsg, setTestMsg] = useState('Bonjour, ceci est un test de la voix synthetique Vocal.');
    const [testKey, setTestKey] = useState('');
    const [testing, setTesting] = useState(false);
    // Logs API publique (v1) — diagnostic WhatsApp & autres endpoints
    const [logs, setLogs] = useState([]);
    const [logsStats, setLogsStats] = useState(null);
    const [logsPage, setLogsPage] = useState(1);
    const [logsTotal, setLogsTotal] = useState(0);
    const [logsFilters, setLogsFilters] = useState({ endpoint: 'POST /v1/messages/whatsapp/template', status: '', q: '' });
    const [logDetail, setLogDetail] = useState(null);

    const loadLogs = useCallback(async (page = 1) => {
        const qs = new URLSearchParams({ page: String(page), limit: '50' });
        if (logsFilters.endpoint) qs.set('endpoint', logsFilters.endpoint);
        if (logsFilters.status)   qs.set('status', logsFilters.status);
        if (logsFilters.q)        qs.set('q', logsFilters.q);
        try {
            const data = await api.get('/api/admin/api-logs?' + qs.toString());
            setLogs(data.logs || []);
            setLogsTotal(data.total || 0);
            setLogsStats(data.stats_24h || null);
            setLogsPage(page);
        } catch (e) { notify.error('Erreur chargement logs'); }
    }, [logsFilters, notify]);

    const openLogDetail = async (id) => {
        try {
            const data = await api.get(`/api/admin/api-logs/${id}`);
            setLogDetail(data.log);
        } catch (e) { notify.error('Erreur détail log'); }
    };

    const loadData = useCallback(async () => {
        setLoading(true);
        try {
            const linesData = await api.get('/api/lines');
            setLines(linesData.lines || []);
        } catch (e) {}
        try {
            const keysData = await api.get('/api/api-keys');
            setKeys(keysData || []);
        } catch (e) {}
        setLoading(false);
    }, []);

    const loadCalls = useCallback(async (page = 1) => {
        try {
            const data = await api.get(`/api/api-calls?page=${page}&limit=20`);
            setCalls(data.calls || []);
            setCallsTotal(data.total || 0);
            setCallsPage(page);
        } catch (e) {}
    }, []);

    useEffect(() => { loadData(); }, []);
    useEffect(() => { if (tab === 'calls') loadCalls(1); }, [tab]);
    useEffect(() => { if (tab === 'logs') loadLogs(1); }, [tab]);

    const openNew = () => { setEditing(null); setForm({ label: '', line_id: '' }); setShowModal(true); };
    const openEdit = (k) => { setEditing(k); setForm({ label: k.label || '', line_id: k.line_id ? String(k.line_id) : '' }); setShowModal(true); };

    const handleSave = async () => {
        if (!form.line_id) { notify.error('Ligne requise'); return; }
        try {
            if (editing) {
                await api.put(`/api/api-keys/${editing.id}`, { label: form.label, line_id: parseInt(form.line_id) });
                notify.success('Clef mise a jour');
            } else {
                await api.post('/api/api-keys', { label: form.label, line_id: parseInt(form.line_id) });
                notify.success('Clef API creee');
            }
            setShowModal(false); loadData();
        } catch (e) { notify.error('Erreur'); }
    };

    const handleToggle = async (k) => {
        await api.put(`/api/api-keys/${k.id}`, { is_active: k.is_active ? 0 : 1 });
        notify.info(k.is_active ? 'Clef desactivee' : 'Clef activee');
        loadData();
    };

    const handleDelete = async (k) => {
        if (!confirm(`Supprimer la clef "${k.label || k.key_value}" ?`)) return;
        await api.del(`/api/api-keys/${k.id}`);
        notify.success('Clef supprimee');
        loadData();
    };

    const copyKey = (key) => {
        navigator.clipboard.writeText(key);
        notify.success('Clef copiee');
    };

    const maskKey = (val) => val ? val.slice(0, 8) + '••••••••••••••••' : '-';

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">API</h1>
                    <p className="page-subtitle">Clefs API et appels sortants programmatiques</p>
                </div>
                {tab === 'keys' && <button className="btn btn-primary" onClick={openNew}><Icons.Plus className="w-4 h-4" /> Nouvelle clef</button>}
            </div>
            <div className="page-content">
                <div className="tabs" style={{ marginBottom: '1.5rem' }}>
                    <button className={`tab ${tab === 'keys' ? 'active' : ''}`} onClick={() => setTab('keys')}>Clefs API</button>
                    <button className={`tab ${tab === 'calls' ? 'active' : ''}`} onClick={() => setTab('calls')}>Appels API</button>
                    <button className={`tab ${tab === 'logs' ? 'active' : ''}`} onClick={() => setTab('logs')}>Logs WhatsApp / v1</button>
                    <button className={`tab ${tab === 'docs' ? 'active' : ''}`} onClick={() => setTab('docs')}>Documentation</button>
                </div>

                {tab === 'keys' && (
                    loading ? <SkeletonTable rows={6} cols={5} /> :
                    keys.length === 0 ? (
                        <div className="empty-state">
                            <Icons.Key className="empty-state-icon" />
                            <h3>Aucune clef API</h3>
                            <p>Creez une clef pour permettre aux applications externes de passer des appels.</p>
                        </div>
                    ) : (
                        <div className="card">
                            <table className="table">
                                <thead><tr><th>Clef</th><th>Label</th><th>Ligne</th><th>Appels</th><th>Statut</th><th style={{ width: '100px' }}>Actions</th></tr></thead>
                                <tbody>
                                    {keys.map(k => (
                                        <tr key={k.id}>
                                            <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem' }}>
                                                <span style={{ cursor: 'pointer' }} onClick={() => setShowSecret(p => ({ ...p, [k.id]: !p[k.id] }))}>
                                                    {showSecret[k.id] ? k.key_value : maskKey(k.key_value)}
                                                </span>
                                                <button onClick={() => copyKey(k.key_value)} style={{ marginLeft: '0.5rem', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--primary)', fontSize: '0.75rem' }}>copier</button>
                                            </td>
                                            <td style={{ fontWeight: 500 }}>{k.label || '-'}</td>
                                            <td><span className="badge badge-info">{k.line_label || k.line_phone || '-'}</span></td>
                                            <td style={{ fontFamily: 'ui-monospace, monospace' }}>{k.calls_count || 0}</td>
                                            <td>
                                                <button onClick={() => handleToggle(k)} className={`badge ${k.is_active ? 'badge-success' : 'badge-gray'}`} style={{ cursor: 'pointer', border: 'none' }}>
                                                    {k.is_active ? 'Active' : 'Inactive'}
                                                </button>
                                            </td>
                                            <td>
                                                <div style={{ display: 'flex', gap: '0.25rem' }}>
                                                    <button className="btn btn-sm" onClick={() => openEdit(k)}><Icons.Edit className="w-4 h-4" /></button>
                                                    <button className="btn btn-sm btn-danger" onClick={() => handleDelete(k)}><Icons.Trash className="w-4 h-4" /></button>
                                                </div>
                                            </td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    )
                )}

                {tab === 'calls' && (
                    <div className="card">
                        {calls.length === 0 ? (
                            <div className="empty-state"><p>Aucun appel API pour l'instant.</p></div>
                        ) : (
                            <>
                                <table className="table">
                                    <thead><tr><th>Date</th><th>Destination</th><th>Ligne</th><th>Voix</th><th>Statut</th><th>Duree</th><th>Cout</th></tr></thead>
                                    <tbody>
                                        {calls.map(c => (
                                            <tr key={c.id}>
                                                <td style={{ fontSize: '0.8rem', whiteSpace: 'nowrap' }}>{formatDateShort(c.created_at)} {new Date(c.created_at).toLocaleTimeString('fr-CH', { hour: '2-digit', minute: '2-digit' })}</td>
                                                <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.85rem' }}>{c.to_number}</td>
                                                <td><span className="badge badge-info">{c.line_label || c.line_phone || '-'}</span></td>
                                                <td style={{ fontSize: '0.8rem' }}>{c.voice}</td>
                                                <td><span className={`badge ${c.status === 'completed' ? 'badge-success' : c.status === 'failed' ? 'badge-danger' : 'badge-gray'}`}>{c.status}</span></td>
                                                <td style={{ fontFamily: 'ui-monospace, monospace' }}>{c.duration || 0}s</td>
                                                <td style={{ fontFamily: 'ui-monospace, monospace' }}>{c.billed_price != null ? `${c.billed_price.toFixed(2)} CHF` : '-'}</td>
                                            </tr>
                                        ))}
                                    </tbody>
                                </table>
                                {callsTotal > 20 && (
                                    <div style={{ display: 'flex', justifyContent: 'center', gap: '0.5rem', padding: '1rem' }}>
                                        <button className="btn btn-sm" disabled={callsPage <= 1} onClick={() => loadCalls(callsPage - 1)}>Precedent</button>
                                        <span style={{ padding: '0.4rem 0.8rem', fontSize: '0.85rem' }}>{callsPage} / {Math.ceil(callsTotal / 20)}</span>
                                        <button className="btn btn-sm" disabled={callsPage >= Math.ceil(callsTotal / 20)} onClick={() => loadCalls(callsPage + 1)}>Suivant</button>
                                    </div>
                                )}
                            </>
                        )}
                    </div>
                )}

                {tab === 'logs' && (
                    <>
                        {logsStats && (
                            <div className="card" style={{ display: 'flex', gap: '2rem', padding: '1rem 1.5rem', marginBottom: '1rem' }}>
                                <div><div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>24h — Total</div><div style={{ fontSize: '1.3rem', fontWeight: 600 }}>{logsStats.total || 0}</div></div>
                                <div><div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>OK (2xx)</div><div style={{ fontSize: '1.3rem', fontWeight: 600, color: 'var(--success, #10b981)' }}>{logsStats.ok || 0}</div></div>
                                <div><div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Erreurs</div><div style={{ fontSize: '1.3rem', fontWeight: 600, color: 'var(--danger, #ef4444)' }}>{logsStats.errors || 0}</div></div>
                                <div><div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Latence moy.</div><div style={{ fontSize: '1.3rem', fontWeight: 600 }}>{logsStats.avg_ms ? Math.round(logsStats.avg_ms) + ' ms' : '-'}</div></div>
                            </div>
                        )}

                        <div className="card" style={{ padding: '1rem 1.5rem', marginBottom: '1rem', display: 'flex', gap: '0.75rem', flexWrap: 'wrap', alignItems: 'center' }}>
                            <select className="form-input" style={{ width: 'auto' }} value={logsFilters.endpoint} onChange={e => setLogsFilters(f => ({ ...f, endpoint: e.target.value }))}>
                                <option value="">Tous les endpoints</option>
                                <option value="POST /v1/messages/whatsapp/template">POST /v1/messages/whatsapp/template</option>
                            </select>
                            <select className="form-input" style={{ width: 'auto' }} value={logsFilters.status} onChange={e => setLogsFilters(f => ({ ...f, status: e.target.value }))}>
                                <option value="">Tous statuts</option>
                                <option value="2xx">2xx (succès)</option>
                                <option value="4xx">4xx (client)</option>
                                <option value="5xx">5xx (serveur)</option>
                                <option value="errors">Erreurs (≥400)</option>
                            </select>
                            <input className="form-input" style={{ width: '220px' }} placeholder="Rechercher (numéro, erreur…)" value={logsFilters.q} onChange={e => setLogsFilters(f => ({ ...f, q: e.target.value }))} />
                            <button className="btn btn-primary btn-sm" onClick={() => loadLogs(1)}>Filtrer</button>
                            <button className="btn btn-sm" onClick={() => loadLogs(logsPage)}>⟳ Actualiser</button>
                        </div>

                        <div className="card">
                            {logs.length === 0 ? (
                                <div className="empty-state"><p>Aucune requête API enregistrée.</p></div>
                            ) : (
                                <table className="table">
                                    <thead><tr>
                                        <th>Date</th><th>Endpoint</th><th>Client</th><th>Clé</th>
                                        <th>De → Vers</th><th>Statut</th><th>Latence</th><th>Erreur</th><th></th>
                                    </tr></thead>
                                    <tbody>
                                        {logs.map(l => {
                                            const sc = l.status_code || 0;
                                            const cls = sc >= 500 ? 'badge-danger' : sc >= 400 ? 'badge-warning' : sc >= 200 ? 'badge-success' : 'badge-gray';
                                            return (
                                                <tr key={l.id} style={{ cursor: 'pointer' }} onClick={() => openLogDetail(l.id)}>
                                                    <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem' }}>{l.created_at}</td>
                                                    <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem' }}>{l.endpoint}</td>
                                                    <td>{l.client_name || (l.client_id ? '#' + l.client_id : '-')}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{l.api_key_label || (l.api_key_id ? '#' + l.api_key_id : '-')}</td>
                                                    <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem' }}>{l.phone_from || '?'} → {l.phone_to || '?'}</td>
                                                    <td><span className={`badge ${cls}`}>{sc || '-'}</span></td>
                                                    <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem' }}>{l.duration_ms ? l.duration_ms + ' ms' : '-'}</td>
                                                    <td style={{ fontSize: '0.75rem', color: 'var(--danger, #ef4444)', maxWidth: '240px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{l.error_message || ''}</td>
                                                    <td><button className="btn btn-sm" onClick={(e) => { e.stopPropagation(); openLogDetail(l.id); }}>Voir</button></td>
                                                </tr>
                                            );
                                        })}
                                    </tbody>
                                </table>
                            )}
                            {logsTotal > 50 && (
                                <div style={{ padding: '1rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                                    <span style={{ fontSize: '0.85rem', color: 'var(--text-muted)' }}>Page {logsPage} / {Math.ceil(logsTotal / 50)} — {logsTotal} requêtes</span>
                                    <div style={{ display: 'flex', gap: '0.5rem' }}>
                                        <button className="btn btn-sm" disabled={logsPage <= 1} onClick={() => loadLogs(logsPage - 1)}>← Précédent</button>
                                        <button className="btn btn-sm" disabled={logsPage >= Math.ceil(logsTotal / 50)} onClick={() => loadLogs(logsPage + 1)}>Suivant →</button>
                                    </div>
                                </div>
                            )}
                        </div>

                        {logDetail && (
                            <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 200, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }} onClick={() => setLogDetail(null)}>
                                <div className="card" style={{ width: 'min(900px, 100%)', maxHeight: '90vh', overflow: 'auto', padding: '1.5rem' }} onClick={e => e.stopPropagation()}>
                                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
                                        <h3 style={{ margin: 0 }}>Requête API #{logDetail.id}</h3>
                                        <button className="btn btn-sm" onClick={() => setLogDetail(null)}>Fermer</button>
                                    </div>
                                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem 1.5rem', marginBottom: '1rem', fontSize: '0.85rem' }}>
                                        <div><strong>Endpoint :</strong> <code>{logDetail.endpoint}</code></div>
                                        <div><strong>Date :</strong> {logDetail.created_at}</div>
                                        <div><strong>Statut :</strong> {logDetail.status_code}</div>
                                        <div><strong>Latence :</strong> {logDetail.duration_ms} ms</div>
                                        <div><strong>Client :</strong> {logDetail.client_name || '-'} (#{logDetail.client_id})</div>
                                        <div><strong>Clé API :</strong> {logDetail.api_key_label || '-'} (#{logDetail.api_key_id})</div>
                                        <div><strong>De :</strong> {logDetail.phone_from || '-'}</div>
                                        <div><strong>Vers :</strong> {logDetail.phone_to || '-'}</div>
                                        <div><strong>IP :</strong> <code>{logDetail.ip_address || '-'}</code></div>
                                        <div style={{ gridColumn: '1 / 3' }}><strong>User-Agent :</strong> <code style={{ fontSize: '0.75rem' }}>{logDetail.user_agent || '-'}</code></div>
                                        {logDetail.error_message && <div style={{ gridColumn: '1 / 3', color: 'var(--danger, #ef4444)' }}><strong>Erreur :</strong> {logDetail.error_message}</div>}
                                    </div>

                                    <div style={{ marginBottom: '1rem' }}>
                                        <div style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Requête (body)</div>
                                        <pre style={{ background: 'var(--bg-secondary)', padding: '1rem', borderRadius: '0.5rem', fontSize: '0.75rem', maxHeight: '300px', overflow: 'auto', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{logDetail.request_body_parsed ? JSON.stringify(logDetail.request_body_parsed, null, 2) : (logDetail.request_body || '(vide)')}</pre>
                                    </div>
                                    <div>
                                        <div style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Réponse</div>
                                        <pre style={{ background: 'var(--bg-secondary)', padding: '1rem', borderRadius: '0.5rem', fontSize: '0.75rem', maxHeight: '300px', overflow: 'auto', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{logDetail.response_body_parsed ? JSON.stringify(logDetail.response_body_parsed, null, 2) : (logDetail.response_body || '(vide)')}</pre>
                                    </div>
                                </div>
                            </div>
                        )}
                    </>
                )}

                {tab === 'docs' && (
                    <div className="card" style={{ padding: '1.5rem' }}>
                        <h3 style={{ marginBottom: '1rem', fontSize: '1.1rem' }}>Passer un appel avec voix synthetique</h3>
                        <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '1rem', fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', lineHeight: 1.6, overflowX: 'auto', border: '1px solid var(--border)' }}>
                            <div style={{ color: 'var(--text-muted)', marginBottom: '0.5rem' }}>POST https://api.helvia.app/v1/call</div>
                            <div style={{ color: 'var(--text-muted)', marginBottom: '0.75rem' }}>Authorization: Bearer vk-votre-clef-api</div>
                            <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{`{
  "to": "+41791234567",
  "message": "Bonjour, ceci est un message automatique.",
  "voice": "Polly.Lea",
  "language": "fr-FR"
}`}</pre>
                        </div>

                        <h4 style={{ marginTop: '1.5rem', marginBottom: '0.5rem', fontSize: '0.95rem' }}>Parametres</h4>
                        <table className="table" style={{ fontSize: '0.85rem' }}>
                            <thead><tr><th>Parametre</th><th>Type</th><th>Requis</th><th>Description</th></tr></thead>
                            <tbody>
                                <tr><td style={{ fontFamily: 'monospace' }}>to</td><td>string</td><td>Oui</td><td>Numero de telephone au format E.164</td></tr>
                                <tr><td style={{ fontFamily: 'monospace' }}>message</td><td>string</td><td>Oui</td><td>Texte a lire par la voix synthetique</td></tr>
                                <tr><td style={{ fontFamily: 'monospace' }}>voice</td><td>string</td><td>Non</td><td>Voix TTS (defaut: Polly.Lea)</td></tr>
                                <tr><td style={{ fontFamily: 'monospace' }}>language</td><td>string</td><td>Non</td><td>Langue (defaut: fr-FR)</td></tr>
                            </tbody>
                        </table>

                        <h4 style={{ marginTop: '1.5rem', marginBottom: '0.5rem', fontSize: '0.95rem' }}>Voix disponibles</h4>
                        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '0.5rem' }}>
                            {[
                                { name: 'Polly.Lea', desc: 'Francais (femme, neural)', lang: 'fr-FR' },
                                { name: 'Polly.Remi', desc: 'Francais (homme, neural)', lang: 'fr-FR' },
                                { name: 'Polly.Celine', desc: 'Francais (femme)', lang: 'fr-FR' },
                                { name: 'Polly.Mathieu', desc: 'Francais (homme)', lang: 'fr-FR' },
                                { name: 'Polly.Joanna', desc: 'Anglais US (femme)', lang: 'en-US' },
                                { name: 'Polly.Matthew', desc: 'Anglais US (homme)', lang: 'en-US' },
                                { name: 'Polly.Amy', desc: 'Anglais UK (femme)', lang: 'en-GB' },
                                { name: 'Polly.Brian', desc: 'Anglais UK (homme)', lang: 'en-GB' },
                                { name: 'Polly.Hans', desc: 'Allemand (homme)', lang: 'de-DE' },
                                { name: 'Polly.Vicki', desc: 'Allemand (femme)', lang: 'de-DE' },
                                { name: 'Polly.Lupe', desc: 'Espagnol (femme)', lang: 'es-US' },
                                { name: 'Polly.Lucia', desc: 'Espagnol EU (femme)', lang: 'es-ES' },
                            ].map(v => (
                                <div key={v.name} style={{ background: testVoice === v.name ? 'var(--primary-light, #ede9fe)' : 'var(--bg-secondary)', padding: '0.5rem 0.75rem', borderRadius: '0.375rem', border: testVoice === v.name ? '2px solid var(--primary)' : '1px solid var(--border)', cursor: 'pointer', transition: 'all 0.15s' }}
                                    onClick={() => setTestVoice(v.name)}>
                                    <div style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: 600 }}>{v.name}</div>
                                    <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{v.desc}</div>
                                </div>
                            ))}
                        </div>

                        <h4 style={{ marginTop: '1.5rem', marginBottom: '0.5rem', fontSize: '0.95rem' }}>Tester une voix</h4>
                        <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '1rem', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
                                <div>
                                    <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.25rem' }}>Clef API</label>
                                    <select className="form-input" value={testKey} onChange={e => setTestKey(e.target.value)} style={{ margin: 0 }}>
                                        <option value="">-- Choisir --</option>
                                        {keys.map(k => <option key={k.id} value={k.key_value}>{k.label || k.key_value.slice(0, 12) + '...'} ({k.line_phone})</option>)}
                                    </select>
                                </div>
                                <div>
                                    <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.25rem' }}>Numero a appeler</label>
                                    <input className="form-input" type="tel" value={testPhone} placeholder="+41 79..." onChange={e => setTestPhone(e.target.value)} style={{ margin: 0 }} />
                                </div>
                            </div>
                            <div>
                                <label style={{ fontSize: '0.75rem', fontWeight: 600, color: 'var(--text-secondary)', display: 'block', marginBottom: '0.25rem' }}>Message de test</label>
                                <textarea className="form-input" rows="2" value={testMsg} onChange={e => setTestMsg(e.target.value)} style={{ margin: 0 }} />
                            </div>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
                                <button className="btn btn-primary" disabled={testing || !testKey || !testPhone || !testMsg}
                                    onClick={async () => {
                                        setTesting(true);
                                        try {
                                            const resp = await fetch('https://api.helvia.app/v1/call', {
                                                method: 'POST',
                                                headers: { 'Authorization': `Bearer ${testKey}`, 'Content-Type': 'application/json' },
                                                body: JSON.stringify({ to: testPhone, message: testMsg, voice: testVoice }),
                                            });
                                            const data = await resp.json();
                                            if (data.success) notify.success(`Appel lance vers ${testPhone} avec ${testVoice}`);
                                            else notify.error(data.error || 'Erreur');
                                        } catch (e) { notify.error('Erreur: ' + e.message); }
                                        finally { setTesting(false); }
                                    }}>
                                    {testing ? 'Appel en cours...' : `Appeler avec ${testVoice}`}
                                </button>
                                <span style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>Un appel reel sera passe (facture)</span>
                            </div>
                        </div>

                        <div style={{ marginTop: '1rem', padding: '0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.375rem', border: '1px solid var(--border)' }}>
                            <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Toutes les voix Amazon Polly : </span>
                            <a href="https://docs.aws.amazon.com/polly/latest/dg/voicelist.html" target="_blank" rel="noopener noreferrer" style={{ fontSize: '0.8rem', color: 'var(--primary)' }}>Documentation Amazon Polly</a>
                            <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}> | </span>
                            <a href="https://www.twilio.com/docs/voice/twiml/say/text-speech#polly-standard-and-neural-voices" target="_blank" rel="noopener noreferrer" style={{ fontSize: '0.8rem', color: 'var(--primary)' }}>Voix Twilio/Polly</a>
                        </div>

                        <h4 style={{ marginTop: '1.5rem', marginBottom: '0.5rem', fontSize: '0.95rem' }}>Reponse</h4>
                        <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '1rem', fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', border: '1px solid var(--border)' }}>
                            <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{`{
  "success": true,
  "call_id": 42,
  "call_sid": "CA...",
  "status": "initiated",
  "to": "+41791234567",
  "from": "+41225391405",
  "voice": "Polly.Lea"
}`}</pre>
                        </div>

                        <h4 style={{ marginTop: '1.5rem', marginBottom: '0.5rem', fontSize: '0.95rem' }}>Exemple cURL</h4>
                        <div style={{ background: 'var(--bg-secondary)', borderRadius: '0.5rem', padding: '1rem', fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', border: '1px solid var(--border)', overflowX: 'auto' }}>
                            <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{`curl -X POST https://api.helvia.app/v1/call \\
  -H "Authorization: Bearer vk-votre-clef-api" \\
  -H "Content-Type: application/json" \\
  -d '{"to":"+41791234567","message":"Bonjour, votre rendez-vous est confirme pour demain a 14h."}'`}</pre>
                        </div>
                    </div>
                )}
            </div>

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal-content" style={{ maxWidth: '440px' }} onClick={e => e.stopPropagation()}>
                        <div className="modal-header">
                            <h3 className="modal-title">{editing ? 'Modifier la clef' : 'Nouvelle clef API'}</h3>
                            <button className="modal-close" onClick={() => setShowModal(false)}>&times;</button>
                        </div>
                        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                            <div className="form-group">
                                <label className="form-label">Label</label>
                                <input className="form-input" value={form.label} placeholder="Ex: App mobile, CRM..."
                                    onChange={(e) => setForm(f => ({ ...f, label: e.target.value }))} />
                            </div>
                            <div className="form-group">
                                <label className="form-label">Ligne associee *</label>
                                <select className="form-input" value={form.line_id} onChange={(e) => setForm(f => ({ ...f, line_id: e.target.value }))}>
                                    <option value="">-- Choisir une ligne --</option>
                                    {lines.map(l => <option key={l.id} value={l.id}>{l.label || l.phone_number} ({l.phone_number})</option>)}
                                </select>
                                <small style={{ color: 'var(--text-muted)', fontSize: '0.75rem' }}>Le numero de cette ligne sera utilise comme expediteur des appels</small>
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={handleSave}>{editing ? 'Enregistrer' : 'Creer'}</button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== SETTINGS VIEW ====================
const SettingsView = () => {
    const { notify } = useApp();
    const [tab, setTab] = useState('hold-music');
    const [musicList, setMusicList] = useState([]);
    const [loading, setLoading] = useState(true);
    const [uploading, setUploading] = useState(false);
    const [editingId, setEditingId] = useState(null);
    const [editName, setEditName] = useState('');
    const [playingId, setPlayingId] = useState(null);
    const audioRef = useRef(null);

    const loadMusic = async () => {
        setLoading(true);
        try {
            const data = await api.get('/api/hold-music');
            setMusicList(Array.isArray(data) ? data : []);
        } catch (e) { notify.error('Erreur chargement'); }
        finally { setLoading(false); }
    };
    useEffect(() => { loadMusic(); }, []);

    const handleUpload = async (e) => {
        const file = e.target.files?.[0];
        if (!file) return;
        if (file.size > 10 * 1024 * 1024) { notify.error('Max 10 Mo'); return; }
        setUploading(true);
        try {
            const formData = new FormData();
            formData.append('file', file);
            formData.append('name', file.name.replace(/\.[^.]+$/, ''));
            const resp = await fetch(CONFIG.API_URL + '/api/hold-music', {
                method: 'POST',
                headers: { 'Authorization': `Bearer ${api.token}` },
                body: formData,
            });
            const data = await resp.json();
            if (data.error) throw new Error(data.error);
            notify.success('Musique ajoutée');
            loadMusic();
        } catch (e) { notify.error('Erreur: ' + e.message); }
        finally { setUploading(false); e.target.value = ''; }
    };

    const handleRename = async (item) => {
        if (!editName.trim()) return;
        try {
            await api.put(`/api/hold-music/${item.id}`, { name: editName.trim() });
            setEditingId(null);
            notify.success('Renommé');
            loadMusic();
        } catch (e) { notify.error('Erreur'); }
    };

    const handleDelete = async (item) => {
        if (!confirm(`Supprimer "${item.name}" ?`)) return;
        try {
            await api.del(`/api/hold-music/${item.id}`);
            notify.success('Supprimé');
            loadMusic();
        } catch (e) { notify.error('Erreur'); }
    };

    const togglePlay = (item) => {
        if (playingId === item.id) {
            audioRef.current?.pause();
            setPlayingId(null);
        } else {
            if (audioRef.current) audioRef.current.pause();
            const a = new Audio(item.file_url);
            a.onended = () => setPlayingId(null);
            a.play();
            audioRef.current = a;
            setPlayingId(item.id);
        }
    };

    const formatSize = (bytes) => {
        if (!bytes) return '-';
        if (bytes < 1024) return bytes + ' o';
        if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' Ko';
        return (bytes / (1024 * 1024)).toFixed(1) + ' Mo';
    };

    return (
        <div className="fade-in">
            <div className="page-header">
                <div>
                    <h1 className="page-title">Paramètres</h1>
                    <p className="page-subtitle">Configuration générale du système</p>
                </div>
            </div>

            <div className="tabs" style={{ marginBottom: '1.5rem' }}>
                <button className={`tab ${tab === 'hold-music' ? 'active' : ''}`} onClick={() => setTab('hold-music')}>
                    Musique d'attente
                </button>
            </div>

            {tab === 'hold-music' && (
                <div style={{ maxWidth: '800px' }}>
                    <div className="card" style={{ padding: '1.5rem', marginBottom: '1.5rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
                            <div>
                                <h3 style={{ fontSize: '0.95rem', fontWeight: 700, margin: 0 }}>Bibliothèque de musiques d'attente</h3>
                                <p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', margin: '4px 0 0' }}>
                                    Uploadez des fichiers MP3 pour les utiliser comme musique d'attente sur vos lignes.
                                </p>
                            </div>
                            <label className="btn btn-primary btn-sm" style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
                                <Icons.Plus className="w-4 h-4" />
                                {uploading ? 'Upload...' : 'Ajouter un MP3'}
                                <input type="file" accept="audio/mpeg,audio/mp3" style={{ display: 'none' }} disabled={uploading} onChange={handleUpload} />
                            </label>
                        </div>

                        {loading ? (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                {Array.from({ length: 4 }).map((_, i) => (
                                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.6rem 0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.5rem' }}>
                                        <Skeleton width={28} height={28} circle />
                                        <div style={{ flex: 1 }}><Skeleton width="40%" height={11} style={{ marginBottom: '0.3rem' }} /><Skeleton width="60%" height={9} /></div>
                                        <Skeleton width={60} height={18} />
                                    </div>
                                ))}
                            </div>
                        ) : musicList.length === 0 ? (
                            <div style={{ textAlign: 'center', padding: '2rem', color: 'var(--text-muted)', background: 'var(--bg-secondary)', borderRadius: '0.5rem' }}>
                                <p style={{ fontSize: '0.9rem', fontWeight: 600 }}>Aucune musique d'attente</p>
                                <p style={{ fontSize: '0.82rem', marginTop: '0.25rem' }}>Ajoutez votre première musique d'attente en cliquant sur "Ajouter un MP3".</p>
                            </div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                                {musicList.map(item => (
                                    <div key={item.id} style={{
                                        display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.75rem 1rem',
                                        background: playingId === item.id ? '#ede9fe' : 'var(--bg-secondary)',
                                        borderRadius: '0.5rem', border: `1px solid ${playingId === item.id ? '#c4b5fd' : 'var(--border)'}`,
                                        transition: 'all 0.15s'
                                    }}>
                                        <button onClick={() => togglePlay(item)} style={{
                                            width: '36px', height: '36px', borderRadius: '50%', border: 'none', cursor: 'pointer',
                                            background: playingId === item.id ? 'var(--primary)' : '#e2e8f0', color: playingId === item.id ? '#fff' : '#64748b',
                                            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, transition: 'all 0.15s'
                                        }}>
                                            {playingId === item.id ? (
                                                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
                                            ) : (
                                                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
                                            )}
                                        </button>

                                        <div style={{ flex: 1, minWidth: 0 }}>
                                            {editingId === item.id ? (
                                                <div style={{ display: 'flex', gap: '0.4rem' }}>
                                                    <input
                                                        type="text" value={editName}
                                                        onChange={e => setEditName(e.target.value)}
                                                        onKeyDown={e => e.key === 'Enter' && handleRename(item)}
                                                        style={{ flex: 1, padding: '4px 8px', fontSize: '0.82rem', borderRadius: '4px', border: '1px solid var(--border)' }}
                                                        autoFocus
                                                    />
                                                    <button className="btn btn-primary btn-sm" onClick={() => handleRename(item)} style={{ padding: '4px 8px', fontSize: '0.72rem' }}>OK</button>
                                                    <button className="btn btn-sm" onClick={() => setEditingId(null)} style={{ padding: '4px 8px', fontSize: '0.72rem' }}>✕</button>
                                                </div>
                                            ) : (
                                                <>
                                                    <div style={{ fontWeight: 600, fontSize: '0.85rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.name}</div>
                                                    <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)', display: 'flex', gap: '0.75rem', marginTop: '2px' }}>
                                                        <span>{formatSize(item.file_size)}</span>
                                                        <span>{formatDate(item.created_at)}</span>
                                                    </div>
                                                </>
                                            )}
                                        </div>

                                        {editingId !== item.id && (
                                            <div style={{ display: 'flex', gap: '0.35rem' }}>
                                                <button className="btn btn-sm" title="Renommer" onClick={() => { setEditingId(item.id); setEditName(item.name); }}
                                                    style={{ padding: '4px 8px', fontSize: '0.72rem' }}>
                                                    <Icons.Edit className="w-3 h-3" />
                                                </button>
                                                <button className="btn btn-danger btn-sm" title="Supprimer" onClick={() => handleDelete(item)}
                                                    style={{ padding: '4px 8px', fontSize: '0.72rem' }}>
                                                    <Icons.Trash className="w-3 h-3" />
                                                </button>
                                            </div>
                                        )}
                                    </div>
                                ))}
                            </div>
                        )}
                    </div>
                </div>
            )}
        </div>
    );
};

// ==================== DIAGNOSTIC VIEW ====================
// Vue admin pour lister les erreurs Cloudflare/D1/Twilio/OpenAI/etc.,
// les marquer comme résolues, ou les renvoyer/tester sur Telegram.

// ==================== ANALYTICS PLATFORM VIEW ====================
// Analyse 360° de tout ce qui se passe sur la plateforme Vocal :
//  - Appels (volume, durée, distribution horaire, statuts, top numéros)
//  - SMS / WhatsApp / conversations multi-canal
//  - Voice bots (usage, messages)
//  - Modules (adoption, revenus)
//  - Croissance (clients/lignes par jour)
//  - Erreurs système (par source, sévérité)
//  - Wallet flow + recharges
const AnalyticsPlatformView = () => {
    const { notify, navigate } = useApp();
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);
    const [period, setPeriod] = useState(30);

    const load = async (silent = false) => {
        if (silent) setRefreshing(true); else setLoading(true);
        try {
            const r = await api.get(`/api/admin/analytics/platform?days=${period}`);
            if (r?.error) throw new Error(r.error);
            setData(r);
        } catch (e) {
            notify.error('Erreur analytics: ' + e.message);
        } finally {
            setLoading(false);
            setRefreshing(false);
        }
    };
    useEffect(() => { load(); /* eslint-disable-next-line */ }, [period]);

    const fmtCHF = (n) => `${(Number(n) || 0).toLocaleString('fr-CH', { maximumFractionDigits: 2 })} CHF`;
    const fmtNum = (n) => (Number(n) || 0).toLocaleString('fr-CH');
    const fmtPct = (n) => `${(Number(n) || 0).toFixed(1)}%`;
    const fmtDur = (sec) => {
        const s = Number(sec) || 0;
        if (s < 60) return `${s} s`;
        if (s < 3600) return `${Math.round(s / 60)} min`;
        if (s < 86400) return `${(s / 3600).toFixed(1)} h`;
        return `${(s / 86400).toFixed(1)} j`;
    };
    const fmtDay = (iso) => {
        try { return new Date(iso + 'T00:00:00').toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit' }); }
        catch { return iso; }
    };

    if (loading) {
        return (
            <>
                <div className="page-header"><h1 className="page-title">Analytics</h1></div>
                <div className="page-content"><SkeletonStatsGrid count={6} /></div>
            </>
        );
    }

    const p = data?.platform || {};
    const live = data?.live || {};
    const calls = data?.calls || {};
    const ck = calls.kpis || {};
    const ts = calls.timeseries || [];
    const byHour = calls.by_hour || [];
    const byWeekday = calls.by_weekday || [];
    const byStatus = calls.by_status || [];
    const topCallers = calls.top_callers || [];
    const topLines = calls.top_lines || [];
    const sms = data?.messaging?.sms || { kpis: {}, timeseries: [] };
    const wa = data?.messaging?.whatsapp || { kpis: {}, timeseries: [] };
    const convChannels = data?.messaging?.conversations_by_channel || [];
    const bots = data?.voice_bots || [];
    const recharges = data?.recharges || {};
    const modulesTop = data?.modules_top || [];
    const growth = data?.growth || {};
    const errs = data?.errors || {};
    const topActivity = data?.top_clients_activity || [];
    const wallet = data?.wallet_flow || [];

    const maxTsCalls = Math.max(1, ...ts.map(d => d.total || 0));
    const maxHour = Math.max(1, ...byHour.map(h => h.n || 0));
    const maxWeekday = Math.max(1, ...byWeekday.map(d => d.n || 0));
    const totalByStatus = byStatus.reduce((s, r) => s + (r.n || 0), 0);
    const callsGrowthColor = (ck.growth_pct || 0) >= 0 ? '#16a34a' : '#dc2626';
    const callsGrowthSign = (ck.growth_pct || 0) >= 0 ? '+' : '';

    const STATUS_COLORS = {
        completed: '#16a34a',
        answered: '#16a34a',
        'in-progress': '#0ea5e9',
        ringing: '#f59e0b',
        queued: '#a855f7',
        'no-answer': '#ea580c',
        busy: '#f97316',
        failed: '#dc2626',
        canceled: '#94a3b8',
        unknown: '#64748b',
    };

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Analytics</h1>
                    <p className="page-subtitle">Analyse 360° de la plateforme : appels, SMS, WhatsApp, bots, modules, erreurs, croissance</p>
                </div>
                <div className="page-actions" style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', alignItems: 'center' }}>
                    <select className="form-input" style={{ width: 170 }} value={period} onChange={(e) => setPeriod(parseInt(e.target.value))}>
                        <option value={7}>7 derniers jours</option>
                        <option value={30}>30 derniers jours</option>
                        <option value={90}>90 derniers jours</option>
                        <option value={180}>6 derniers mois</option>
                        <option value={365}>12 derniers mois</option>
                    </select>
                    <button className="btn btn-secondary btn-sm" onClick={() => load(true)} disabled={refreshing}>
                        <Icons.Refresh className="w-4 h-4" /> {refreshing ? 'Sync...' : 'Actualiser'}
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={() => navigate('analytics')}>
                        <Icons.Sparkles className="w-4 h-4" /> Analytics IA
                    </button>
                </div>
            </div>

            <div className="page-content">
                {/* ==================== Plateforme — overview tiles ==================== */}
                <div style={{ marginBottom: '0.5rem', fontSize: '0.78rem', fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                    Plateforme · all-time
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: '0.6rem', marginBottom: '1.25rem' }}>
                    <StatCard label="Clients (actifs)"     value={`${fmtNum(p.clients_active)} / ${fmtNum(p.clients_total)}`} color="#2563eb" icon={<Icons.Users className="w-5 h-5" />} />
                    <StatCard label="Lignes (actives)"     value={`${fmtNum(p.lines_active)} / ${fmtNum(p.lines_total)}`}     color="#16a34a" icon={<Icons.Phone className="w-5 h-5" />} />
                    <StatCard label="Appels (all-time)"    value={fmtNum(p.calls_all_time)}                                   color="#ea580c" icon={<Icons.PhoneIncoming className="w-5 h-5" />} />
                    <StatCard label="SMS (all-time)"       value={fmtNum(p.sms_all_time)}                                     color="#f59e0b" icon={<Icons.Send className="w-5 h-5" />} />
                    <StatCard label="WhatsApp (all-time)"  value={fmtNum(p.whatsapp_all_time)}                                color="#10b981" icon={<Icons.MessageSquare className="w-5 h-5" />} />
                    <StatCard label="Conversations"        value={fmtNum(p.conversations_total)}                              color="#8b5cf6" icon={<Icons.MessageSquare className="w-5 h-5" />} />
                    <StatCard label="Voice bots actifs"    value={`${fmtNum(p.voice_bots_active)} / ${fmtNum(p.voice_bots_total)}`} color="#a855f7" icon={<Icons.Bot className="w-5 h-5" />} />
                    <StatCard label="Modules actifs"       value={fmtNum(p.modules_active)}                                   color="#0d9488" icon={<Icons.CreditCard className="w-5 h-5" />} />
                    <StatCard label="Partenaires"          value={fmtNum(p.partners_active)}                                  color="#475569" icon={<Icons.Link className="w-5 h-5" />} />
                    <StatCard label="Comptes Twilio"       value={fmtNum(p.twilio_accounts)}                                  color="#dc2626" icon={<Icons.Server className="w-5 h-5" />} />
                    <StatCard label="Appels en cours"      value={fmtNum(live.active_calls_now)}                              color="#0ea5e9" icon={<Icons.Activity className="w-5 h-5" />} />
                    <StatCard label="Sessions actives"     value={fmtNum(live.sessions_active)}                               color="#6366f1" icon={<Icons.Globe className="w-5 h-5" />} />
                </div>

                {/* ==================== APPELS — KPIs période ==================== */}
                <div style={{ marginBottom: '0.5rem', fontSize: '0.78rem', fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                    📞 Appels · {period} derniers jours
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: '0.6rem', marginBottom: '1rem' }}>
                    <StatCard label={`Total appels`}            value={fmtNum(ck.total)}             color="#6366f1" icon={<Icons.PhoneIncoming className="w-5 h-5" />} />
                    <StatCard label={`vs période précédente`}   value={`${callsGrowthSign}${fmtPct(ck.growth_pct)}`} color={callsGrowthColor} icon={<Icons.TrendingUp className="w-5 h-5" />} />
                    <StatCard label="Entrants"                  value={fmtNum(ck.inbound)}           color="#16a34a" icon={<Icons.PhoneIncoming className="w-5 h-5" />} />
                    <StatCard label="Sortants"                  value={fmtNum(ck.outbound)}          color="#0284c7" icon={<Icons.Phone className="w-5 h-5" />} />
                    <StatCard label="Décrochés"                 value={fmtNum(ck.answered)}          color="#16a34a" icon={<Icons.Check className="w-5 h-5" />} />
                    <StatCard label="Manqués / échec"           value={fmtNum(ck.missed)}            color="#dc2626" icon={<Icons.AlertTriangle className="w-5 h-5" />} />
                    <StatCard label="Durée totale"              value={fmtDur(ck.duration_sec)}      color="#a855f7" icon={<Icons.Clock className="w-5 h-5" />} />
                    <StatCard label="Durée moy. / appel"        value={fmtDur(ck.avg_duration_sec)}  color="#0d9488" icon={<Icons.Activity className="w-5 h-5" />} />
                    <StatCard label="Facturé clients"           value={fmtCHF(ck.billed_chf)}        color="#16a34a" icon={<Icons.DollarSign className="w-5 h-5" />} />
                    <StatCard label="Coût Twilio (USD)"         value={`${fmtNum(ck.cost_usd)} $`}    color="#ea580c" icon={<Icons.CreditCard className="w-5 h-5" />} />
                </div>

                {/* ==================== Timeseries appels ==================== */}
                <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                    <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.6rem' }}>
                        Volume d'appels par jour ({period}j)
                    </div>
                    <div style={{ display: 'flex', alignItems: 'flex-end', gap: '2px', height: 160, padding: '0 2px' }}>
                        {ts.map((d, i) => {
                            const inH = (d.inbound  / maxTsCalls) * 100;
                            const ouH = (d.outbound / maxTsCalls) * 100;
                            const showLabel = (period <= 30 ? (i % 3 === 0) : (i % 10 === 0)) || i === ts.length - 1;
                            return (
                                <div key={d.day} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }} title={`${d.day} : ${d.total} (${d.inbound} in, ${d.outbound} out)`}>
                                    <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
                                        <div style={{ width: '100%', height: `${ouH}%`, background: '#0284c7', borderRadius: '2px 2px 0 0', opacity: d.outbound === 0 ? 0.15 : 1 }} />
                                        <div style={{ width: '100%', height: `${inH}%`, background: '#16a34a', borderRadius: ouH > 0 ? 0 : '2px 2px 0 0', borderBottomLeftRadius: 2, borderBottomRightRadius: 2, opacity: d.inbound === 0 ? 0.15 : 1 }} />
                                    </div>
                                    <div style={{ fontSize: '0.6rem', color: '#94a3b8' }}>
                                        {showLabel ? fmtDay(d.day) : ''}
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                    <div style={{ display: 'flex', gap: '1.25rem', justifyContent: 'center', marginTop: '0.75rem', fontSize: '0.75rem', color: '#64748b' }}>
                        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#16a34a', borderRadius: 2, marginRight: 6 }} /> Entrants</span>
                        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#0284c7', borderRadius: 2, marginRight: 6 }} /> Sortants</span>
                    </div>
                </div>

                {/* ==================== Distribution horaire + jour de semaine ==================== */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: '1rem', marginBottom: '1rem' }}>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            🕐 Distribution horaire (heure de la journée)
                        </div>
                        <div style={{ display: 'flex', alignItems: 'flex-end', gap: '3px', height: 120 }}>
                            {byHour.map(h => {
                                const height = (h.n / maxHour) * 100;
                                const peak = h.n === maxHour && maxHour > 0;
                                return (
                                    <div key={h.hour} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }} title={`${h.hour}h : ${h.n} appels`}>
                                        <div style={{
                                            width: '100%', height: `${Math.max(2, height)}%`,
                                            background: peak ? '#dc2626' : '#6366f1',
                                            borderRadius: '3px 3px 1px 1px',
                                            opacity: h.n === 0 ? 0.15 : 1,
                                        }} />
                                        <div style={{ fontSize: '0.6rem', color: '#94a3b8', fontWeight: peak ? 700 : 400 }}>
                                            {h.hour % 3 === 0 ? `${h.hour}h` : ''}
                                        </div>
                                    </div>
                                );
                            })}
                        </div>
                    </div>

                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            📅 Distribution par jour de la semaine
                        </div>
                        <div style={{ display: 'flex', alignItems: 'flex-end', gap: '8px', height: 120 }}>
                            {byWeekday.map(d => {
                                const height = (d.n / maxWeekday) * 100;
                                const peak = d.n === maxWeekday && maxWeekday > 0;
                                return (
                                    <div key={d.dow} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }}>
                                        <div style={{
                                            width: '100%', height: `${Math.max(2, height)}%`,
                                            background: peak ? '#16a34a' : '#0d9488',
                                            borderRadius: '4px 4px 2px 2px',
                                            opacity: d.n === 0 ? 0.15 : 1,
                                        }} title={`${d.label} : ${d.n} appels`} />
                                        <div style={{ fontSize: '0.7rem', color: '#475569', fontWeight: peak ? 700 : 500 }}>{d.label}</div>
                                        <div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>{fmtNum(d.n)}</div>
                                    </div>
                                );
                            })}
                        </div>
                    </div>
                </div>

                {/* ==================== Statuts d'appels + tops ==================== */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: '1rem', marginBottom: '1rem' }}>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            🚦 Statuts d'appels ({fmtNum(totalByStatus)} sur {period}j)
                        </div>
                        {byStatus.length === 0 ? (
                            <div style={{ color: '#94a3b8', fontSize: '0.85rem' }}>Aucun appel sur la période.</div>
                        ) : (
                            <>
                                <div style={{ display: 'flex', height: 16, borderRadius: 8, overflow: 'hidden', marginBottom: '0.85rem', background: '#f1f5f9' }}>
                                    {byStatus.map(s => {
                                        const w = totalByStatus ? (s.n / totalByStatus) * 100 : 0;
                                        return <div key={s.status} style={{ width: `${w}%`, background: STATUS_COLORS[s.status] || '#64748b' }} title={`${s.status} : ${s.n}`} />;
                                    })}
                                </div>
                                <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                    {byStatus.map(s => (
                                        <div key={s.status} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.8rem' }}>
                                            <span style={{ width: 10, height: 10, borderRadius: 2, background: STATUS_COLORS[s.status] || '#64748b' }} />
                                            <span style={{ flex: 1, color: '#0f172a', fontWeight: 600 }}>{s.status}</span>
                                            <span style={{ color: '#64748b' }}>{fmtNum(s.n)}</span>
                                            <span style={{ color: '#94a3b8', minWidth: 50, textAlign: 'right' }}>
                                                {totalByStatus ? `${((s.n / totalByStatus) * 100).toFixed(1)}%` : '–'}
                                            </span>
                                        </div>
                                    ))}
                                </div>
                            </>
                        )}
                    </div>

                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            📞 Top 10 numéros appelants
                        </div>
                        {topCallers.length === 0 ? (
                            <div style={{ color: '#94a3b8', fontSize: '0.85rem' }}>Aucune donnée.</div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                {topCallers.map((tc, i) => (
                                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', padding: '0.4rem 0.6rem', background: '#f8fafc', borderRadius: 6 }}>
                                        <span style={{ fontSize: '0.78rem', fontWeight: 700, color: '#64748b', minWidth: 22 }}>#{i + 1}</span>
                                        <span style={{ flex: 1, fontFamily: 'ui-monospace, monospace', fontSize: '0.82rem', color: '#0f172a' }}>{tc.from_number}</span>
                                        <span style={{ fontSize: '0.78rem', color: '#64748b' }}>{fmtDur(tc.duration)}</span>
                                        <span style={{ fontSize: '0.85rem', fontWeight: 700, color: '#6366f1', minWidth: 38, textAlign: 'right' }}>{fmtNum(tc.n)}</span>
                                    </div>
                                ))}
                            </div>
                        )}
                    </div>
                </div>

                {/* ==================== Top lignes ==================== */}
                {topLines.length > 0 && (
                    <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            ☎️ Top 10 lignes les plus chargées
                        </div>
                        <div style={{ overflowX: 'auto' }}>
                            <table style={{ width: '100%', fontSize: '0.82rem', borderCollapse: 'collapse' }}>
                                <thead>
                                    <tr style={{ borderBottom: '1px solid var(--border)', background: '#f8fafc' }}>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Numéro</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Label</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Client</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Appels</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Durée</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {topLines.map(l => (
                                        <tr key={l.id} style={{ borderBottom: '1px solid var(--border)' }}>
                                            <td style={{ padding: '0.5rem 0.75rem', fontFamily: 'ui-monospace, monospace' }}>{l.phone_number || '–'}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', color: '#64748b' }}>{l.label || '–'}</td>
                                            <td style={{ padding: '0.5rem 0.75rem' }}>{l.client_name || `#${l.client_id || '?'}`}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#6366f1' }}>{fmtNum(l.calls)}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', color: '#64748b' }}>{fmtDur(l.duration)}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    </div>
                )}

                {/* ==================== MESSAGING : SMS + WhatsApp + canaux ==================== */}
                <div style={{ marginBottom: '0.5rem', fontSize: '0.78rem', fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                    💬 Messaging
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '1rem', marginBottom: '1rem' }}>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }}>
                            <div style={{ fontSize: '0.85rem', fontWeight: 700, color: '#0f172a' }}>SMS envoyés</div>
                            <span style={{ fontSize: '1.4rem', fontWeight: 800, color: '#f59e0b' }}>{fmtNum(sms.kpis.total)}</span>
                        </div>
                        <MiniSparkline points={sms.timeseries} valueKey="n" color="#f59e0b" />
                    </div>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }}>
                            <div style={{ fontSize: '0.85rem', fontWeight: 700, color: '#0f172a' }}>WhatsApp envoyés</div>
                            <span style={{ fontSize: '1.4rem', fontWeight: 800, color: '#10b981' }}>{fmtNum(wa.kpis.total)}</span>
                        </div>
                        <MiniSparkline points={wa.timeseries} valueKey="n" color="#10b981" />
                    </div>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.85rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.6rem' }}>Conversations multi-canal</div>
                        {convChannels.length === 0 ? (
                            <div style={{ fontSize: '0.78rem', color: '#94a3b8' }}>Aucune conversation sur la période.</div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                {convChannels.map(ch => (
                                    <div key={ch.channel} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.78rem' }}>
                                        <span style={{ flex: 1, fontWeight: 600, color: '#0f172a' }}>{ch.channel}</span>
                                        <span style={{ color: '#16a34a' }}>● {ch.open_n || 0}</span>
                                        <span style={{ color: '#dc2626' }}>↑ {ch.escalated_n || 0}</span>
                                        <span style={{ color: '#64748b', minWidth: 40, textAlign: 'right', fontWeight: 700 }}>{fmtNum(ch.n)}</span>
                                    </div>
                                ))}
                            </div>
                        )}
                    </div>
                </div>

                {/* ==================== Voice bots ==================== */}
                {bots.length > 0 && (
                    <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            🤖 Voice bots — usage sur {period}j
                        </div>
                        <div style={{ overflowX: 'auto' }}>
                            <table style={{ width: '100%', fontSize: '0.82rem', borderCollapse: 'collapse' }}>
                                <thead>
                                    <tr style={{ borderBottom: '1px solid var(--border)', background: '#f8fafc' }}>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Bot</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'center', fontWeight: 700, color: '#475569' }}>Statut</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Conversations</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Messages bot</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {bots.map(b => (
                                        <tr key={b.id} style={{ borderBottom: '1px solid var(--border)' }}>
                                            <td style={{ padding: '0.5rem 0.75rem', fontWeight: 600 }}>{b.name || `#${b.id}`}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'center' }}>
                                                <span style={{
                                                    fontSize: '0.7rem', fontWeight: 700, padding: '2px 8px', borderRadius: 999,
                                                    background: b.is_active ? '#dcfce7' : '#fee2e2',
                                                    color: b.is_active ? '#166534' : '#991b1b',
                                                }}>{b.is_active ? 'ACTIF' : 'INACTIF'}</span>
                                            </td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', color: '#64748b' }}>{fmtNum(b.conversations)}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#a855f7' }}>{fmtNum(b.messages)}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    </div>
                )}

                {/* ==================== Recharges + modules ==================== */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: '1rem', marginBottom: '1rem' }}>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            💳 Recharges (wallet) — {period}j
                        </div>
                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.85rem' }}>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Revenus</div>
                                <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#16a34a' }}>{fmtCHF(recharges.revenue)}</div>
                            </div>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Bonus offerts</div>
                                <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#0891b2' }}>{fmtCHF(recharges.bonus_offered)}</div>
                            </div>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Tentatives</div>
                                <div style={{ fontSize: '1.1rem', fontWeight: 700, color: '#0f172a' }}>{fmtNum(recharges.attempts)}</div>
                            </div>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Conversion</div>
                                <div style={{ fontSize: '1.1rem', fontWeight: 700, color: '#f59e0b' }}>{fmtPct(recharges.conversion_pct)}</div>
                            </div>
                        </div>
                    </div>

                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            📦 Top modules par adoption
                        </div>
                        {modulesTop.length === 0 ? (
                            <div style={{ fontSize: '0.85rem', color: '#94a3b8' }}>Aucun module configuré.</div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                {modulesTop.slice(0, 8).map(m => (
                                    <div key={m.code} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.8rem' }}>
                                        <span style={{ flex: 1, color: '#0f172a', fontWeight: 600 }}>{m.name}</span>
                                        <span style={{ color: '#64748b', fontSize: '0.72rem' }}>{fmtCHF(m.price_chf)}/{m.billing_period === 'year' ? 'an' : 'mois'}</span>
                                        <span style={{ color: '#16a34a', fontWeight: 700 }}>{fmtNum(m.active_subs)} actifs</span>
                                        {m.trial_subs > 0 && <span style={{ color: '#f59e0b', fontSize: '0.72rem' }}>+{m.trial_subs} essai</span>}
                                    </div>
                                ))}
                            </div>
                        )}
                    </div>
                </div>

                {/* ==================== Croissance + erreurs ==================== */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: '1rem', marginBottom: '1rem' }}>
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.6rem' }}>
                            📈 Croissance ({period}j)
                        </div>
                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.85rem', marginBottom: '0.85rem' }}>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase' }}>Nouveaux clients</div>
                                <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#2563eb' }}>{fmtNum(growth.new_clients_total)}</div>
                            </div>
                            <div>
                                <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase' }}>Nouvelles lignes</div>
                                <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#16a34a' }}>{fmtNum(growth.new_lines_total)}</div>
                            </div>
                        </div>
                        <MiniSparkline points={growth.new_clients_timeseries || []} valueKey="n" color="#2563eb" label="clients/jour" />
                    </div>

                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                            <span>⚠️ Erreurs système ({period}j)</span>
                            <span style={{ fontSize: '0.78rem', fontWeight: 700, padding: '2px 8px', borderRadius: 999, background: errs.unresolved_total > 0 ? '#fee2e2' : '#dcfce7', color: errs.unresolved_total > 0 ? '#991b1b' : '#166534' }}>
                                {fmtNum(errs.unresolved_total)} non résolues
                            </span>
                        </div>
                        {(errs.by_source || []).length === 0 ? (
                            <div style={{ fontSize: '0.85rem', color: '#94a3b8' }}>✅ Aucune erreur sur la période.</div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                {(errs.by_source || []).map(e => (
                                    <div key={e.source} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.78rem' }}>
                                        <span style={{ flex: 1, fontWeight: 600, color: '#0f172a' }}>{e.source}</span>
                                        {e.critical > 0 && <span style={{ color: '#7f1d1d', fontWeight: 700 }}>{e.critical} crit.</span>}
                                        {e.errors > 0 && <span style={{ color: '#dc2626' }}>{e.errors} err.</span>}
                                        {e.warnings > 0 && <span style={{ color: '#f59e0b' }}>{e.warnings} warn.</span>}
                                        <span style={{ color: '#64748b', minWidth: 40, textAlign: 'right' }}>{fmtNum(e.total)}</span>
                                    </div>
                                ))}
                            </div>
                        )}
                    </div>
                </div>

                {/* ==================== Top clients par activité ==================== */}
                {topActivity.length > 0 && (
                    <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            🏆 Top 10 clients par activité ({period}j)
                        </div>
                        <div style={{ overflowX: 'auto' }}>
                            <table style={{ width: '100%', fontSize: '0.82rem', borderCollapse: 'collapse' }}>
                                <thead>
                                    <tr style={{ borderBottom: '1px solid var(--border)', background: '#f8fafc' }}>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Client</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'left', fontWeight: 700, color: '#475569' }}>Email</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Appels</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Durée</th>
                                        <th style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#475569' }}>Revenu</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {topActivity.map(tc => (
                                        <tr key={tc.id} style={{ borderBottom: '1px solid var(--border)' }}>
                                            <td style={{ padding: '0.5rem 0.75rem', fontWeight: 600 }}>{tc.name || `#${tc.id}`}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', color: '#64748b' }}>{tc.email || '–'}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#6366f1' }}>{fmtNum(tc.calls)}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', color: '#64748b' }}>{fmtDur(tc.duration_sec)}</td>
                                            <td style={{ padding: '0.5rem 0.75rem', textAlign: 'right', fontWeight: 700, color: '#16a34a' }}>{fmtCHF(tc.revenue)}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    </div>
                )}

                {/* ==================== Wallet flow ==================== */}
                {wallet.length > 0 && (
                    <div className="card" style={{ padding: '1.25rem', marginBottom: '1rem' }}>
                        <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.85rem' }}>
                            💰 Flux wallet par type ({period}j)
                        </div>
                        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: '0.5rem' }}>
                            {wallet.map(w => (
                                <div key={w.type} style={{ padding: '0.6rem 0.85rem', background: '#f8fafc', borderRadius: 8, border: '1px solid var(--border)' }}>
                                    <div style={{ fontSize: '0.7rem', color: '#64748b', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.04em' }}>{w.type}</div>
                                    <div style={{ fontSize: '1rem', fontWeight: 800, color: w.total >= 0 ? '#16a34a' : '#dc2626', marginTop: 2 }}>
                                        {w.total >= 0 ? '+' : ''}{fmtCHF(w.total)}
                                    </div>
                                    <div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>{fmtNum(w.n)} ops</div>
                                </div>
                            ))}
                        </div>
                    </div>
                )}
            </div>
        </>
    );
};

// Mini sparkline réutilisable pour les timeseries (SMS/WA/clients/jour)
const MiniSparkline = ({ points = [], valueKey = 'n', color = '#6366f1', label }) => {
    if (!points || points.length === 0) {
        return <div style={{ fontSize: '0.75rem', color: '#94a3b8', padding: '0.5rem 0' }}>Aucune donnée.</div>;
    }
    const max = Math.max(1, ...points.map(p => Number(p[valueKey]) || 0));
    return (
        <div>
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: '2px', height: 50 }}>
                {points.map((p, i) => {
                    const v = Number(p[valueKey]) || 0;
                    const h = Math.max(2, (v / max) * 100);
                    return (
                        <div key={i} title={`${p.day}: ${v}`} style={{
                            flex: 1, height: `${h}%`, background: color, borderRadius: '2px 2px 1px 1px',
                            opacity: v === 0 ? 0.18 : 1,
                        }} />
                    );
                })}
            </div>
            {label && <div style={{ fontSize: '0.7rem', color: '#94a3b8', marginTop: 4, textAlign: 'right' }}>{label}</div>}
        </div>
    );
};

// ==================== QUICK RECOS (computed locally from data, no AI cost) ====================
// Génère des recommandations instantanées et déterministes basées sur les chiffres réels.
// Complémentaire des "Idées IA" (gpt-4o-mini) : visible immédiatement, gratuit, reproductible.
function computeQuickRecos(data) {
    if (!data) return [];
    const recos = [];
    const k = data.kpis || {};
    const packs = data.packs || [];
    const catalog = data.pack_catalog || [];
    const modules = data.modules || [];
    const top = data.top_clients || [];
    const subs = data.subs_by_plan || [];
    const dist = data.recharge_distribution || [];
    const totalRevenue = Number(k.total_revenue) || 0;
    const fmtCHF = (n) => `${(Number(n) || 0).toLocaleString('fr-CH', { maximumFractionDigits: 2 })} CHF`;
    const fmtPct = (n) => `${(Number(n) || 0).toFixed(1)}%`;

    // ---------- PACKS de recharge ----------
    const purchasedIds = new Set(packs.map(p => p.pack_id));
    const deadPacks = catalog.filter(p => !purchasedIds.has(p.id));
    if (deadPacks.length > 0) {
        recos.push({
            severity: 'medium', category: 'packs',
            title: `${deadPacks.length} pack(s) jamais acheté(s) sur la période`,
            desc: `Ces packs n'ont généré aucune commande sur la période : ${deadPacks.map(p => p.label).join(', ')}. Envisagez de les retirer du catalogue, de les renommer ou de revoir leur tarif/bonus pour les rendre plus attractifs.`,
            anchor: 'packs',
        });
    }

    if (totalRevenue > 0 && packs.length > 0) {
        const top1 = packs[0];
        const share = (top1.revenue / totalRevenue) * 100;
        if (share >= 25) {
            const label = catalog.find(c => c.id === top1.pack_id)?.label || top1.pack_id;
            recos.push({
                severity: 'suggestion', category: 'pricing',
                title: `Pack vedette : ${label}`,
                desc: `Ce pack représente ${share.toFixed(1)}% du revenu sur les recharges (${fmtCHF(top1.revenue)} sur ${top1.count} achats). Vous pouvez tester un prix légèrement plus haut ou réduire le bonus pour augmenter la marge — mesurez l'impact via A/B sur 30 jours.`,
                anchor: 'packs',
            });
        }
    }

    if (catalog.length > 0) {
        const best = [...catalog].sort((a, b) => (b.bonus_pct || 0) - (a.bonus_pct || 0))[0];
        if (best && best.bonus_pct > 0) {
            recos.push({
                severity: 'suggestion', category: 'packs',
                title: `Meilleur rapport prix/crédit : ${best.label}`,
                desc: `Ce pack offre ${fmtPct(best.bonus_pct)} de bonus (${fmtCHF(best.total_credited)} crédités pour ${fmtCHF(best.amount)} payés). Mettez-le en avant dans l'écran "Recharger" comme "Meilleur rapport".`,
                anchor: 'packs',
            });
        }
    }

    const overBonus = packs.filter(p => p.revenue > 0 && (p.bonus / p.revenue) > 0.30);
    if (overBonus.length > 0) {
        const worst = overBonus.sort((a, b) => (b.bonus / b.revenue) - (a.bonus / a.revenue))[0];
        const ratio = (worst.bonus / worst.revenue) * 100;
        recos.push({
            severity: 'medium', category: 'pricing',
            title: `Bonus élevé sur ${worst.pack_id} (${ratio.toFixed(0)}% du revenu offert)`,
            desc: `Vous offrez ${fmtCHF(worst.bonus)} de bonus pour ${fmtCHF(worst.revenue)} encaissés sur ce pack — votre marge est rongée. Réduisez le bonus de moitié et observez l'impact sur le volume avant de revenir en arrière.`,
            anchor: 'packs',
        });
    }

    // ---------- MODULES (apps activables) ----------
    const deadModules = modules.filter(m => (m.subscribers_active || 0) === 0 && (m.is_active === undefined || m.is_active === 1 || m.is_active === true));
    if (deadModules.length > 0) {
        recos.push({
            severity: 'medium', category: 'modules',
            title: `${deadModules.length} module(s) sans activation payante`,
            desc: `Aucune activation active sur la période pour : ${deadModules.slice(0, 4).map(m => m.name).join(', ')}${deadModules.length > 4 ? '…' : ''}. Hypothèses : prix trop élevé pour la valeur perçue, module non découvert, ou usage non explicite. Ajoutez un essai gratuit 14j, ou une démo intégrée dans my.helvia.app.`,
            anchor: 'modules',
        });
    }

    const churned = modules.filter(m => {
        const denom = (m.subscribers_active || 0) + (m.subscribers_cancelled || 0);
        return denom >= 3 && (m.subscribers_cancelled || 0) / denom >= 0.4;
    });
    for (const m of churned.slice(0, 2)) {
        const denom = (m.subscribers_active || 0) + (m.subscribers_cancelled || 0);
        const churnPct = ((m.subscribers_cancelled || 0) / denom) * 100;
        recos.push({
            severity: 'high', category: 'churn',
            title: `Churn élevé sur le module ${m.name} (${churnPct.toFixed(0)}%)`,
            desc: `${m.subscribers_cancelled} clients ont annulé contre ${m.subscribers_active} encore actifs. Lancez un sondage 1-question aux churners ("pourquoi avez-vous désactivé ${m.name} ?") et envisagez un downgrade tier moins cher.`,
            anchor: 'modules',
        });
    }

    const trialHeavy = modules.filter(m => (m.subscribers_trial || 0) > 0 && (m.subscribers_trial || 0) > (m.subscribers_active || 0) * 1.5);
    for (const m of trialHeavy.slice(0, 2)) {
        recos.push({
            severity: 'high', category: 'conversion',
            title: `Conversion trial→paid faible sur ${m.name}`,
            desc: `${m.subscribers_trial} en essai vs seulement ${m.subscribers_active} payants. Activez un email J-3 avant fin d'essai avec un screencast 30s + témoignage client. Testez aussi un essai plus court (7j au lieu de 14j) — l'urgence convertit.`,
            anchor: 'modules',
        });
    }

    const topPotential = [...modules].sort((a, b) => (b.potential_mrr || 0) - (a.potential_mrr || 0))[0];
    if (topPotential && topPotential.potential_mrr > 0) {
        recos.push({
            severity: 'suggestion', category: 'modules',
            title: `Star module : ${topPotential.name}`,
            desc: `${topPotential.subscribers_active} clients actifs × ${fmtCHF(topPotential.price_chf)}/mois = ${fmtCHF(topPotential.potential_mrr)} de MRR récurrent. Mettez ce module en bandeau sur la home de my.helvia.app et créez un cas-client public pour booster les conversions.`,
            anchor: 'modules',
        });
    }

    // ---------- KPIs / cross-cutting ----------
    if (k.attempted_purchases >= 5 && k.conversion_rate < 60) {
        recos.push({
            severity: 'high', category: 'conversion',
            title: `Conversion checkout faible (${fmtPct(k.conversion_rate)})`,
            desc: `${k.successful_purchases}/${k.attempted_purchases} tentatives Stripe ont abouti. ${(100 - (k.conversion_rate || 0)).toFixed(0)}% des clients abandonnent au paiement. Vérifiez les rejets carte 3DS, ajoutez TWINT/Apple Pay/Google Pay, et raccourcissez le formulaire.`,
            anchor: 'top',
        });
    }

    if (k.growth_pct <= -10 && k.total_revenue_previous > 100) {
        recos.push({
            severity: 'high', category: 'growth',
            title: `Croissance en baisse (${fmtPct(k.growth_pct)} vs période précédente)`,
            desc: `Revenu période actuelle : ${fmtCHF(k.total_revenue)} contre ${fmtCHF(k.total_revenue_previous)} sur la précédente. Identifiez les 5 plus gros clients qui ont moins rechargé et déclenchez un mail / appel commercial. Lancez aussi une promo bonus +10% temporaire.`,
            anchor: 'top',
        });
    } else if (k.growth_pct >= 30) {
        recos.push({
            severity: 'suggestion', category: 'growth',
            title: `Croissance forte (+${fmtPct(k.growth_pct)})`,
            desc: `Capitalisez : préparez un plan de capacité (Twilio, OpenAI quotas), demandez 3 témoignages aux clients qui ont le plus consommé ce mois, et augmentez votre budget acquisition de 20-30%.`,
            anchor: 'top',
        });
    }

    if (k.repeat_purchase_rate_pct > 0 && k.repeat_purchase_rate_pct < 20 && k.paying_clients_period >= 5) {
        recos.push({
            severity: 'medium', category: 'retention',
            title: `Faible taux de re-recharge (${fmtPct(k.repeat_purchase_rate_pct)})`,
            desc: `Seuls ${fmtPct(k.repeat_purchase_rate_pct)} de vos clients ont rechargé plus d'une fois sur la période. Activez un seuil "wallet < 5 CHF" → email/Slack auto avec lien direct vers le checkout du dernier pack utilisé.`,
            anchor: 'top',
        });
    }

    if (k.total_recharged_all_time > 0) {
        const outRatio = (k.total_balance_outstanding / k.total_recharged_all_time) * 100;
        if (outRatio > 25) {
            recos.push({
                severity: 'medium', category: 'risk',
                title: `Wallet outstanding élevé (${fmtCHF(k.total_balance_outstanding)})`,
                desc: `${outRatio.toFixed(0)}% de ce qui a été rechargé all-time est encore dans les wallets clients. Risque de remboursement et capital immobilisé. Ajoutez une expiration douce (12 mois) ou poussez la consommation via SMS marketing.`,
                anchor: 'wallet',
            });
        }
    }

    if (totalRevenue > 0 && k.bonus_offered > 0) {
        const bonusRatio = (k.bonus_offered / totalRevenue) * 100;
        if (bonusRatio > 25) {
            recos.push({
                severity: 'low', category: 'pricing',
                title: `Bonus offerts agressifs (${bonusRatio.toFixed(0)}% du revenu)`,
                desc: `${fmtCHF(k.bonus_offered)} offerts pour ${fmtCHF(totalRevenue)} encaissés. Réduisez le bonus de 5 points sur les 2 plus gros packs et observez le volume — souvent neutre, mais marge en hausse.`,
                anchor: 'packs',
            });
        }
    }

    // ---------- TOP CLIENTS ----------
    if (totalRevenue > 0 && top.length > 0) {
        const top1 = top[0];
        const share1 = (top1.revenue / totalRevenue) * 100;
        if (share1 >= 30 && top.length >= 3) {
            recos.push({
                severity: 'high', category: 'risk',
                title: `Dépendance à un client (${share1.toFixed(0)}% du revenu)`,
                desc: `${top1.name || 'Le top client'} représente ${fmtCHF(top1.revenue)} sur ${fmtCHF(totalRevenue)}. Risque business majeur s'il churne. Sécurisez par un contrat annuel + diversifiez via acquisition ciblée sur le même secteur.`,
                anchor: 'topclients',
            });
        }
        if (top.length >= 5) {
            const top5Share = top.slice(0, 5).reduce((s, c) => s + (c.revenue || 0), 0) / totalRevenue * 100;
            if (top5Share >= 70) {
                recos.push({
                    severity: 'medium', category: 'risk',
                    title: `Top 5 clients = ${top5Share.toFixed(0)}% du revenu`,
                    desc: `Concentration élevée. Lancez un programme "client ambassadeur" pour ces comptes (remise 5%, accès beta) en échange d'un témoignage ou d'une intro à 2 prospects.`,
                    anchor: 'topclients',
                });
            }
        }
    }

    // ---------- ABONNEMENTS ----------
    const monthly = subs.filter(s => s.status === 'active' && s.billing_period === 'month').reduce((s, x) => s + (x.n || 0), 0);
    const yearly  = subs.filter(s => s.status === 'active' && s.billing_period === 'year').reduce((s, x) => s + (x.n || 0), 0);
    if (monthly >= 10 && yearly / Math.max(1, monthly) < 0.10) {
        recos.push({
            severity: 'suggestion', category: 'pricing',
            title: `Pas assez d'abonnements annuels (${yearly}/${monthly} mensuels)`,
            desc: `Seuls ${yearly} clients sur ${monthly + yearly} sont en annuel. Mettez en avant l'économie réelle ("économisez 47 CHF/an") sur la page "Abonnement" + ajoutez une étape upsell après la 3e recharge mensuelle.`,
            anchor: 'subs',
        });
    }

    // ---------- DISTRIBUTION ----------
    const totalRecharges = dist.reduce((s, b) => s + (b.count || 0), 0);
    if (totalRecharges >= 10) {
        const small = (dist.find(b => b.label === '0-9')?.count || 0);
        const big = (dist.find(b => b.label === '500+')?.count || 0);
        if (small / totalRecharges > 0.40) {
            recos.push({
                severity: 'low', category: 'packs',
                title: `Trop de petites recharges (${((small / totalRecharges) * 100).toFixed(0)}% < 10 CHF)`,
                desc: `${small} recharges sur ${totalRecharges} sont sous 10 CHF — overhead Stripe/admin élevé. Augmentez le minimum à 10 CHF ou créez un pack "starter 10 CHF + 2 offerts" pour pousser le panier moyen.`,
                anchor: 'packs',
            });
        }
        if (big === 0 && totalRecharges >= 20) {
            recos.push({
                severity: 'suggestion', category: 'pricing',
                title: `Aucune recharge ≥ 500 CHF sur la période`,
                desc: `Vos plus gros clients ne saturent pas le ceiling. Créez un pack "Pro 500 CHF + 100 offerts" et ciblez par email les 10 plus gros consommateurs.`,
                anchor: 'packs',
            });
        }
    }

    // Tri : high > medium > low > suggestion
    const order = { high: 0, medium: 1, low: 2, suggestion: 3 };
    recos.sort((a, b) => (order[a.severity] ?? 9) - (order[b.severity] ?? 9));
    return recos;
}

const QUICK_RECO_BADGES = {
    high:       { bg: '#fee2e2', color: '#991b1b', label: 'PRIORITÉ HAUTE' },
    medium:     { bg: '#fef3c7', color: '#92400e', label: 'PRIORITÉ MOYENNE' },
    low:        { bg: '#e0f2fe', color: '#075985', label: 'À CONSIDÉRER' },
    suggestion: { bg: '#ede9fe', color: '#5b21b6', label: 'SUGGESTION' },
};
const QUICK_RECO_CAT_COLORS = {
    pricing: '#6366f1', packs: '#0891b2', modules: '#a855f7',
    conversion: '#f59e0b', retention: '#16a34a', churn: '#dc2626',
    growth: '#0284c7', risk: '#ea580c',
};

function QuickRecosPanel({ data }) {
    const [collapsed, setCollapsed] = useState(false);
    const [filter, setFilter] = useState('all');
    const recos = useMemo(() => computeQuickRecos(data), [data]);

    const counts = useMemo(() => ({
        all: recos.length,
        high: recos.filter(r => r.severity === 'high').length,
        medium: recos.filter(r => r.severity === 'medium').length,
        low: recos.filter(r => r.severity === 'low').length,
        suggestion: recos.filter(r => r.severity === 'suggestion').length,
    }), [recos]);

    const visible = filter === 'all' ? recos : recos.filter(r => r.severity === filter);

    const scrollTo = (anchor) => {
        if (!anchor || anchor === 'top') { window.scrollTo({ top: 0, behavior: 'smooth' }); return; }
        const el = document.getElementById(`analytics-anchor-${anchor}`);
        if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    if (!data) return null;

    return (
        <div className="card" style={{ marginBottom: '1.5rem', overflow: 'hidden', borderTop: '4px solid #6366f1' }}>
            <div style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                padding: '1rem 1.25rem', borderBottom: collapsed ? 'none' : '1px solid #e2e8f0',
                gap: '1rem', flexWrap: 'wrap',
            }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flex: 1, minWidth: 240 }}>
                    <div style={{
                        width: 38, height: 38, borderRadius: 10,
                        background: 'linear-gradient(135deg, #6366f1, #a855f7)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', flexShrink: 0,
                    }}>
                        <Icons.Lightbulb className="w-5 h-5" />
                    </div>
                    <div>
                        <div style={{ fontWeight: 800, fontSize: '1rem', color: '#0f172a' }}>Recos rapides</div>
                        <div style={{ fontSize: '0.75rem', color: '#64748b' }}>
                            Calculées en local sur vos chiffres réels — instantané, gratuit, complète les "Idées IA".
                        </div>
                    </div>
                </div>

                <div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap', alignItems: 'center' }}>
                    {[
                        { k: 'all', lbl: 'Total', color: '#0f172a' },
                        { k: 'high', lbl: 'Priorité haute', color: '#991b1b' },
                        { k: 'medium', lbl: 'Priorité moyenne', color: '#92400e' },
                        { k: 'suggestion', lbl: 'Suggestions', color: '#5b21b6' },
                    ].map(s => (
                        <button key={s.k}
                            onClick={() => setFilter(s.k)}
                            style={{
                                background: filter === s.k ? s.color : '#fff',
                                color: filter === s.k ? '#fff' : s.color,
                                border: `1px solid ${s.color}`,
                                padding: '0.35rem 0.7rem', borderRadius: 8,
                                fontSize: '0.72rem', fontWeight: 700, cursor: 'pointer',
                                display: 'flex', alignItems: 'center', gap: '0.4rem',
                            }}
                            title={`Filtrer : ${s.lbl}`}
                        >
                            <span style={{ fontSize: '1rem', fontWeight: 800 }}>{counts[s.k]}</span>
                            <span style={{ opacity: 0.85 }}>{s.lbl}</span>
                        </button>
                    ))}
                    <button onClick={() => setCollapsed(c => !c)}
                        className="btn btn-secondary btn-sm"
                        style={{ marginLeft: '0.25rem' }}
                        title={collapsed ? 'Afficher les recommandations' : 'Masquer'}
                    >
                        {collapsed ? <Icons.ChevronDown className="w-4 h-4" /> : <Icons.ChevronUp className="w-4 h-4" />}
                    </button>
                </div>
            </div>

            {!collapsed && (
                <div style={{ padding: '1rem 1.25rem', display: 'grid', gap: '0.75rem' }}>
                    {visible.length === 0 ? (
                        <div style={{ padding: '1.5rem', textAlign: 'center', color: '#64748b', fontSize: '0.85rem', background: '#f8fafc', borderRadius: 10 }}>
                            {recos.length === 0
                                ? '🎉 Aucune anomalie détectée sur cette période — vos chiffres sont propres.'
                                : 'Aucune reco dans cette catégorie. Changez le filtre.'}
                        </div>
                    ) : visible.map((r, i) => {
                        const sev = QUICK_RECO_BADGES[r.severity] || QUICK_RECO_BADGES.low;
                        const catColor = QUICK_RECO_CAT_COLORS[r.category] || '#475569';
                        return (
                            <div key={i} style={{
                                background: '#fff',
                                border: '1px solid #e2e8f0',
                                borderLeft: `4px solid ${sev.color}`,
                                borderRadius: 10,
                                padding: '0.85rem 1rem',
                            }}>
                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.45rem', flexWrap: 'wrap', marginBottom: '0.4rem' }}>
                                    <span style={{
                                        background: sev.bg, color: sev.color,
                                        padding: '0.2rem 0.55rem', borderRadius: 6,
                                        fontSize: '0.62rem', fontWeight: 800, letterSpacing: '0.04em',
                                    }}>{sev.label}</span>
                                    <span style={{
                                        background: `${catColor}1a`, color: catColor,
                                        padding: '0.2rem 0.55rem', borderRadius: 6,
                                        fontSize: '0.62rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em',
                                    }}>{r.category}</span>
                                </div>
                                <div style={{ fontSize: '0.95rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.3rem' }}>{r.title}</div>
                                <div style={{ fontSize: '0.82rem', color: '#475569', lineHeight: 1.55, marginBottom: '0.5rem' }}>{r.desc}</div>
                                <button onClick={() => scrollTo(r.anchor)}
                                    style={{
                                        background: 'none', border: 'none', padding: 0, cursor: 'pointer',
                                        color: '#6366f1', fontSize: '0.78rem', fontWeight: 700,
                                        display: 'inline-flex', alignItems: 'center', gap: '0.25rem',
                                    }}>
                                    Voir les données <Icons.ArrowRight className="w-3.5 h-3.5" />
                                </button>
                            </div>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

// ==================== CLIENT OFFERS PANEL (per-client smart offers, rules-based) ====================
// Affiche pour chaque client les offres pertinentes (modules à activer, packs, upgrades, win-back).
// Données via GET /api/admin/analytics/client-offers?days=N — chargement à la demande (collapsé par défaut).
const OFFER_TYPE_META = {
    module:           { color: '#a855f7', label: 'Module',         icon: 'Sparkles' },
    bot:              { color: '#6366f1', label: 'Bot vocal',      icon: 'Bot' },
    pack:             { color: '#0891b2', label: 'Pack',           icon: 'CreditCard' },
    recharge_urgent:  { color: '#dc2626', label: 'Wallet bas',     icon: 'AlertTriangle' },
    reallocation:     { color: '#0d9488', label: 'Crédit dormant', icon: 'Wallet' },
    upgrade_annual:   { color: '#059669', label: 'Upgrade annuel', icon: 'TrendingUp' },
    winback:          { color: '#ea580c', label: 'Win-back',       icon: 'Refresh' },
    onboarding:       { color: '#0284c7', label: 'Onboarding',     icon: 'Users' },
    reactivation:     { color: '#7c3aed', label: 'Réactivation',   icon: 'Refresh' },
};
const OFFER_URGENCY = {
    high:   { bg: '#fee2e2', color: '#991b1b', label: 'PRIORITÉ HAUTE' },
    medium: { bg: '#fef3c7', color: '#92400e', label: 'PRIORITÉ MOYENNE' },
    low:    { bg: '#e0f2fe', color: '#075985', label: 'À CONSIDÉRER' },
};

function ClientOffersPanel({ period, notify }) {
    const [collapsed, setCollapsed] = useState(true);
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(false);
    const [search, setSearch] = useState('');
    const [urgencyFilter, setUrgencyFilter] = useState('all');
    const [typeFilter, setTypeFilter] = useState('all');
    const [expandedClient, setExpandedClient] = useState(null);
    const [dismissed, setDismissed] = useState(() => {
        try { return JSON.parse(localStorage.getItem('vocal_adm_offers_dismissed') || '{}'); }
        catch (e) { return {}; }
    });

    const persistDismissed = (next) => {
        setDismissed(next);
        try { localStorage.setItem('vocal_adm_offers_dismissed', JSON.stringify(next)); } catch (e) {}
    };
    const offerKey = (clientId, code) => `${clientId}::${code}`;
    const isDismissed = (clientId, code) => !!dismissed[offerKey(clientId, code)];
    const dismiss = (clientId, code) => {
        const next = { ...dismissed, [offerKey(clientId, code)]: Date.now() };
        persistDismissed(next);
    };
    const undismiss = (clientId, code) => {
        const next = { ...dismissed }; delete next[offerKey(clientId, code)];
        persistDismissed(next);
    };

    const load = async () => {
        setLoading(true);
        try {
            const r = await api.get(`/api/admin/analytics/client-offers?days=${period}`);
            if (r?.error) throw new Error(r.error);
            setData(r);
        } catch (e) {
            notify.error('Erreur offres clients : ' + e.message);
        } finally { setLoading(false); }
    };

    useEffect(() => {
        if (!collapsed) load();
        // eslint-disable-next-line
    }, [collapsed, period]);

    const fmtCHF = (n) => `${(Number(n) || 0).toLocaleString('fr-CH', { maximumFractionDigits: 2 })} CHF`;

    const filteredClients = useMemo(() => {
        if (!data?.clients) return [];
        const q = search.trim().toLowerCase();
        return data.clients
            .map(c => ({
                ...c,
                offers: c.offers.filter(o => {
                    if (urgencyFilter !== 'all' && o.urgency !== urgencyFilter) return false;
                    if (typeFilter !== 'all' && o.type !== typeFilter) return false;
                    return true;
                }),
            }))
            .filter(c => c.offers.length > 0)
            .filter(c => {
                if (!q) return true;
                return (c.name || '').toLowerCase().includes(q)
                    || (c.email || '').toLowerCase().includes(q)
                    || (c.company || '').toLowerCase().includes(q);
            });
    }, [data, search, urgencyFilter, typeFilter]);

    const visibleStats = useMemo(() => {
        let totalOffers = 0;
        let highUrg = 0;
        let potentialMrr = 0;
        let activeOffers = 0;
        let dismissedOffers = 0;
        for (const c of filteredClients) {
            for (const o of c.offers) {
                totalOffers++;
                if (isDismissed(c.client_id, o.code)) { dismissedOffers++; continue; }
                activeOffers++;
                if (o.urgency === 'high') highUrg++;
                potentialMrr += (o.monthly_value_chf || 0);
            }
        }
        return { totalOffers, activeOffers, dismissedOffers, highUrg, potentialMrr };
    }, [filteredClients, dismissed]);

    const buildMailto = (client, offer) => {
        const subject = encodeURIComponent(`Vocal — ${offer.title}`);
        const body = encodeURIComponent(
            `Bonjour ${client.name || ''},

${offer.desc}

→ ${offer.cta}

Disponible dans my.helvia.app ou directement en réponse à ce mail.

L'équipe Vocal
https://helvia.app`
        );
        return `mailto:${encodeURIComponent(client.email || '')}?subject=${subject}&body=${body}`;
    };

    const copyOfferText = async (client, offer) => {
        const text = `${client.name || client.email}\n${offer.title}\n${offer.desc}\nCTA : ${offer.cta}`;
        try { await navigator.clipboard.writeText(text); notify.success('Offre copiée'); }
        catch (e) { notify.error('Copie impossible'); }
    };

    const exportCsv = () => {
        if (!filteredClients.length) return notify.error('Aucune ligne à exporter');
        const rows = [['Client', 'Email', 'Phone', 'Wallet', 'Urgence', 'Type', 'Titre', 'Recommandation', 'CTA', 'MRR potentiel CHF']];
        for (const c of filteredClients) {
            for (const o of c.offers) {
                if (isDismissed(c.client_id, o.code)) continue;
                rows.push([
                    c.name || '', c.email || '', c.phone || '',
                    String(c.balance_chf), o.urgency, o.type,
                    o.title, o.desc, o.cta, String(o.monthly_value_chf || 0),
                ]);
            }
        }
        const csv = rows.map(r => r.map(v => `"${String(v).replace(/"/g, '""').replace(/\n/g, ' ')}"`).join(',')).join('\n');
        const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url; a.download = `vocal-client-offers-${period}j-${new Date().toISOString().slice(0, 10)}.csv`;
        document.body.appendChild(a); a.click(); document.body.removeChild(a);
        URL.revokeObjectURL(url);
    };

    return (
        <div className="card" style={{ marginBottom: '1.5rem', overflow: 'hidden', borderTop: '4px solid #0d9488' }}>
            <div style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                padding: '1rem 1.25rem', borderBottom: collapsed ? 'none' : '1px solid #e2e8f0',
                gap: '1rem', flexWrap: 'wrap',
            }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flex: 1, minWidth: 240 }}>
                    <div style={{
                        width: 38, height: 38, borderRadius: 10,
                        background: 'linear-gradient(135deg, #0d9488, #14b8a6)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', flexShrink: 0,
                    }}>
                        <Icons.Users className="w-5 h-5" />
                    </div>
                    <div>
                        <div style={{ fontWeight: 800, fontSize: '1rem', color: '#0f172a' }}>
                            Offres intelligentes par client
                        </div>
                        <div style={{ fontSize: '0.75rem', color: '#64748b' }}>
                            Recommandations personnalisées (modules, packs, upgrades, win-back) calculées sur l'usage réel.
                        </div>
                    </div>
                </div>

                <div style={{ display: 'flex', gap: '0.4rem', alignItems: 'center', flexWrap: 'wrap' }}>
                    {data && !collapsed && (
                        <>
                            <span style={{ fontSize: '0.75rem', color: '#475569' }}>
                                <strong>{visibleStats.activeOffers}</strong> offres actives ·
                                <strong style={{ color: '#dc2626', marginLeft: 4 }}>{visibleStats.highUrg}</strong> urgentes ·
                                potentiel <strong style={{ color: '#0d9488' }}>{fmtCHF(visibleStats.potentialMrr)}/mois</strong>
                            </span>
                            <button className="btn btn-secondary btn-sm" onClick={load} disabled={loading}>
                                <Icons.Refresh className="w-4 h-4" /> {loading ? 'Sync…' : 'Actualiser'}
                            </button>
                        </>
                    )}
                    <button onClick={() => setCollapsed(c => !c)}
                        className="btn btn-primary btn-sm">
                        {collapsed ? <><Icons.ChevronDown className="w-4 h-4" /> Voir les offres</> : <><Icons.ChevronUp className="w-4 h-4" /> Masquer</>}
                    </button>
                </div>
            </div>

            {!collapsed && (
                <div style={{ padding: '1rem 1.25rem' }}>
                    {loading ? (
                        <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8' }}>
                            Calcul des offres pour tous les clients…
                        </div>
                    ) : !data ? null : data.clients.length === 0 ? (
                        <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8' }}>
                            Aucune offre détectée — tous vos clients ont déjà ce qu'il leur faut 🎉
                        </div>
                    ) : (
                        <>
                            {/* Toolbar : recherche + filtres + export */}
                            <div style={{
                                display: 'flex', gap: '0.5rem', flexWrap: 'wrap',
                                marginBottom: '1rem', alignItems: 'center',
                            }}>
                                <input
                                    className="form-input"
                                    placeholder="Filtrer par nom, email, société…"
                                    value={search}
                                    onChange={(e) => setSearch(e.target.value)}
                                    style={{ minWidth: 220, flex: 1, maxWidth: 380 }}
                                />
                                <select className="form-input" value={urgencyFilter} onChange={(e) => setUrgencyFilter(e.target.value)} style={{ width: 170 }}>
                                    <option value="all">Toutes urgences</option>
                                    <option value="high">Priorité haute</option>
                                    <option value="medium">Priorité moyenne</option>
                                    <option value="low">À considérer</option>
                                </select>
                                <select className="form-input" value={typeFilter} onChange={(e) => setTypeFilter(e.target.value)} style={{ width: 200 }}>
                                    <option value="all">Tous les types</option>
                                    {Object.entries(OFFER_TYPE_META).map(([k, v]) => (
                                        <option key={k} value={k}>{v.label}</option>
                                    ))}
                                </select>
                                <button className="btn btn-secondary btn-sm" onClick={exportCsv} title="Exporter en CSV">
                                    <Icons.Download className="w-4 h-4" /> CSV
                                </button>
                                {visibleStats.dismissedOffers > 0 && (
                                    <button className="btn btn-secondary btn-sm" onClick={() => persistDismissed({})}
                                        title="Réafficher toutes les offres masquées">
                                        Restaurer ({visibleStats.dismissedOffers}) masquées
                                    </button>
                                )}
                            </div>

                            {/* Banner summary */}
                            <div style={{
                                display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))',
                                gap: '0.5rem', marginBottom: '1rem',
                            }}>
                                <div style={{ background: '#f0fdfa', padding: '0.7rem 0.9rem', borderRadius: 10, border: '1px solid #99f6e4' }}>
                                    <div style={{ fontSize: '0.7rem', color: '#0f766e', fontWeight: 700, letterSpacing: '0.05em' }}>CLIENTS CIBLES</div>
                                    <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#134e4a' }}>{filteredClients.length}</div>
                                    <div style={{ fontSize: '0.7rem', color: '#0f766e' }}>sur {data.summary.clients_analyzed} analysés</div>
                                </div>
                                <div style={{ background: '#fef2f2', padding: '0.7rem 0.9rem', borderRadius: 10, border: '1px solid #fecaca' }}>
                                    <div style={{ fontSize: '0.7rem', color: '#991b1b', fontWeight: 700, letterSpacing: '0.05em' }}>URGENT</div>
                                    <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#7f1d1d' }}>{visibleStats.highUrg}</div>
                                    <div style={{ fontSize: '0.7rem', color: '#991b1b' }}>offres priorité haute</div>
                                </div>
                                <div style={{ background: '#fff7ed', padding: '0.7rem 0.9rem', borderRadius: 10, border: '1px solid #fed7aa' }}>
                                    <div style={{ fontSize: '0.7rem', color: '#92400e', fontWeight: 700, letterSpacing: '0.05em' }}>OFFRES ACTIVES</div>
                                    <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#7c2d12' }}>{visibleStats.activeOffers}</div>
                                    <div style={{ fontSize: '0.7rem', color: '#92400e' }}>+ {visibleStats.dismissedOffers} masquées</div>
                                </div>
                                <div style={{ background: '#ecfdf5', padding: '0.7rem 0.9rem', borderRadius: 10, border: '1px solid #a7f3d0' }}>
                                    <div style={{ fontSize: '0.7rem', color: '#065f46', fontWeight: 700, letterSpacing: '0.05em' }}>POTENTIEL MRR</div>
                                    <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#064e3b' }}>{fmtCHF(visibleStats.potentialMrr)}</div>
                                    <div style={{ fontSize: '0.7rem', color: '#065f46' }}>/ mois si 100% closé</div>
                                </div>
                            </div>

                            {/* Liste clients */}
                            <div style={{ display: 'grid', gap: '0.75rem' }}>
                                {filteredClients.map(c => {
                                    const isExpanded = expandedClient === c.client_id;
                                    const visibleOffers = c.offers.filter(o => !isDismissed(c.client_id, o.code));
                                    const dismissedCount = c.offers.length - visibleOffers.length;
                                    if (visibleOffers.length === 0 && dismissedCount === 0) return null;
                                    return (
                                        <div key={c.client_id} style={{
                                            border: '1px solid #e2e8f0', borderRadius: 12, overflow: 'hidden',
                                            background: '#fff',
                                        }}>
                                            <button
                                                onClick={() => setExpandedClient(isExpanded ? null : c.client_id)}
                                                style={{
                                                    width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                                    padding: '0.85rem 1rem', background: '#f8fafc', border: 'none',
                                                    cursor: 'pointer', textAlign: 'left', gap: '1rem', flexWrap: 'wrap',
                                                }}>
                                                <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flex: 1, minWidth: 180 }}>
                                                    <div style={{
                                                        width: 36, height: 36, borderRadius: 10,
                                                        background: 'linear-gradient(135deg, #6366f1, #a855f7)',
                                                        color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
                                                        fontWeight: 700, fontSize: '0.9rem', flexShrink: 0,
                                                    }}>
                                                        {(c.name || c.email || '?')[0].toUpperCase()}
                                                    </div>
                                                    <div style={{ minWidth: 0 }}>
                                                        <div style={{ fontWeight: 700, color: '#0f172a', fontSize: '0.92rem' }}>
                                                            {c.name || '(sans nom)'} {c.company && <span style={{ color: '#64748b', fontWeight: 500 }}>· {c.company}</span>}
                                                        </div>
                                                        <div style={{ fontSize: '0.75rem', color: '#64748b' }}>
                                                            {c.email || '—'} · wallet <strong>{fmtCHF(c.balance_chf)}</strong> · inscrit il y a {c.days_since_signup}j
                                                        </div>
                                                    </div>
                                                </div>
                                                <div style={{ display: 'flex', gap: '0.4rem', alignItems: 'center' }}>
                                                    <span style={{
                                                        background: OFFER_URGENCY[c.max_urgency]?.bg || '#f1f5f9',
                                                        color: OFFER_URGENCY[c.max_urgency]?.color || '#475569',
                                                        padding: '0.2rem 0.55rem', borderRadius: 6,
                                                        fontSize: '0.62rem', fontWeight: 800, letterSpacing: '0.04em',
                                                    }}>{OFFER_URGENCY[c.max_urgency]?.label || c.max_urgency}</span>
                                                    <span style={{
                                                        background: '#ecfdf5', color: '#065f46',
                                                        padding: '0.2rem 0.55rem', borderRadius: 6,
                                                        fontSize: '0.7rem', fontWeight: 700,
                                                    }}>+{fmtCHF(c.potential_mrr_chf)}/mo</span>
                                                    <span style={{
                                                        background: '#eef2ff', color: '#3730a3',
                                                        padding: '0.2rem 0.55rem', borderRadius: 6,
                                                        fontSize: '0.7rem', fontWeight: 700,
                                                    }}>{visibleOffers.length} offre{visibleOffers.length > 1 ? 's' : ''}{dismissedCount > 0 ? ` (+${dismissedCount} ✕)` : ''}</span>
                                                    {isExpanded ? <Icons.ChevronUp className="w-4 h-4" /> : <Icons.ChevronDown className="w-4 h-4" />}
                                                </div>
                                            </button>

                                            {isExpanded && (
                                                <div style={{ padding: '1rem', display: 'grid', gap: '0.6rem' }}>
                                                    {/* Snapshot client */}
                                                    <div style={{
                                                        background: '#f8fafc', borderRadius: 8, padding: '0.6rem 0.8rem',
                                                        fontSize: '0.75rem', color: '#475569', display: 'flex', flexWrap: 'wrap', gap: '0.85rem',
                                                    }}>
                                                        <span><strong>{c.snapshot.calls_period}</strong> appels ({c.snapshot.calls_inbound} in / {c.snapshot.calls_missed} miss)</span>
                                                        <span><strong>{c.snapshot.calls_with_recording}</strong> voicemails</span>
                                                        <span><strong>{c.snapshot.recharges_period}</strong> recharges ({fmtCHF(c.snapshot.avg_basket)} moy.)</span>
                                                        <span><strong>{c.snapshot.lines_active}</strong> lignes actives</span>
                                                        <span><strong>{c.snapshot.bots_active}</strong> bots</span>
                                                        <span>Modules : {c.snapshot.active_modules.length > 0 ? c.snapshot.active_modules.join(', ') : '—'}</span>
                                                    </div>

                                                    {c.offers.map((o, i) => {
                                                        const dismissed = isDismissed(c.client_id, o.code);
                                                        const urg = OFFER_URGENCY[o.urgency] || OFFER_URGENCY.low;
                                                        const meta = OFFER_TYPE_META[o.type] || OFFER_TYPE_META.module;
                                                        return (
                                                            <div key={i} style={{
                                                                border: '1px solid #e2e8f0',
                                                                borderLeft: `4px solid ${urg.color}`,
                                                                borderRadius: 10, padding: '0.75rem 0.9rem',
                                                                opacity: dismissed ? 0.45 : 1,
                                                                background: dismissed ? '#f8fafc' : '#fff',
                                                            }}>
                                                                <div style={{ display: 'flex', gap: '0.45rem', alignItems: 'center', flexWrap: 'wrap', marginBottom: '0.4rem' }}>
                                                                    <span style={{
                                                                        background: urg.bg, color: urg.color,
                                                                        padding: '0.18rem 0.5rem', borderRadius: 6,
                                                                        fontSize: '0.6rem', fontWeight: 800, letterSpacing: '0.04em',
                                                                    }}>{urg.label}</span>
                                                                    <span style={{
                                                                        background: `${meta.color}1a`, color: meta.color,
                                                                        padding: '0.18rem 0.5rem', borderRadius: 6,
                                                                        fontSize: '0.6rem', fontWeight: 700, letterSpacing: '0.04em',
                                                                    }}>{meta.label}</span>
                                                                    <span style={{ fontSize: '0.7rem', color: '#0d9488', fontWeight: 700 }}>
                                                                        +{fmtCHF(o.monthly_value_chf || 0)}/mois
                                                                    </span>
                                                                </div>
                                                                <div style={{ fontSize: '0.92rem', fontWeight: 700, color: '#0f172a', marginBottom: '0.25rem' }}>{o.title}</div>
                                                                <div style={{ fontSize: '0.82rem', color: '#475569', lineHeight: 1.55, marginBottom: '0.5rem' }}>{o.desc}</div>
                                                                <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.4rem', alignItems: 'center' }}>
                                                                    <span style={{ fontSize: '0.78rem', color: '#0d9488', fontWeight: 700, marginRight: 'auto' }}>
                                                                        → {o.cta}
                                                                    </span>
                                                                    {!dismissed && (
                                                                        <>
                                                                            {c.email && (
                                                                                <a className="btn btn-primary btn-sm" href={buildMailto(c, o)} target="_blank" rel="noopener">
                                                                                    <Icons.Mail className="w-3.5 h-3.5" /> Mail
                                                                                </a>
                                                                            )}
                                                                            <button className="btn btn-secondary btn-sm" onClick={() => copyOfferText(c, o)}>
                                                                                <Icons.Copy className="w-3.5 h-3.5" /> Copier
                                                                            </button>
                                                                            <button className="btn btn-secondary btn-sm" onClick={() => dismiss(c.client_id, o.code)} title="Marquer comme traité">
                                                                                <Icons.Check className="w-3.5 h-3.5" /> Traité
                                                                            </button>
                                                                        </>
                                                                    )}
                                                                    {dismissed && (
                                                                        <button className="btn btn-secondary btn-sm" onClick={() => undismiss(c.client_id, o.code)}>
                                                                            Restaurer
                                                                        </button>
                                                                    )}
                                                                </div>
                                                            </div>
                                                        );
                                                    })}
                                                </div>
                                            )}
                                        </div>
                                    );
                                })}
                            </div>

                            <div style={{ marginTop: '0.75rem', fontSize: '0.7rem', color: '#94a3b8', textAlign: 'right' }}>
                                Généré le {data?.generated_at ? new Date(data.generated_at).toLocaleString('fr-FR') : '-'} · période {data?.period_days}j ·
                                {' '}{data.summary.clients_with_offers}/{data.summary.clients_analyzed} clients ciblés ·
                                {' '}{data.summary.total_offers} offres totales · potentiel <strong>{fmtCHF(data.summary.total_potential_mrr_chf)}/mois</strong>.
                                Les statuts "traité" sont sauvegardés localement par admin (localStorage).
                            </div>
                        </>
                    )}
                </div>
            )}
        </div>
    );
}

// ==================== ANALYTICS VIEW (revenue, packs, subs, modules + IA insights) ====================
const AnalyticsView = () => {
    const { notify } = useApp();
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);
    const [period, setPeriod] = useState(30);
    const [insights, setInsights] = useState(null);
    const [insightsLoading, setInsightsLoading] = useState(false);
    const [focus, setFocus] = useState('');

    const load = async (silent = false) => {
        if (silent) setRefreshing(true); else setLoading(true);
        try {
            const r = await api.get(`/api/admin/analytics/overview?days=${period}`);
            if (r?.error) throw new Error(r.error);
            setData(r);
        } catch (e) {
            notify.error('Erreur analytics: ' + e.message);
        } finally {
            setLoading(false);
            setRefreshing(false);
        }
    };

    useEffect(() => { load(); /* eslint-disable-next-line */ }, [period]);

    const generateInsights = async () => {
        setInsightsLoading(true);
        try {
            const r = await api.post('/api/admin/analytics/insights', { days: period, focus: focus || undefined });
            if (r?.error) throw new Error(r.error);
            setInsights(r);
            notify.success(`${(r.insights || []).length} idées d'amélioration générées`);
        } catch (e) {
            notify.error('IA: ' + e.message);
        } finally {
            setInsightsLoading(false);
        }
    };

    const fmtCHF = (n) => `${(Number(n) || 0).toLocaleString('fr-CH', { maximumFractionDigits: 2 })} CHF`;
    const fmtNum = (n) => (Number(n) || 0).toLocaleString('fr-CH');
    const fmtPct = (n) => `${(Number(n) || 0).toFixed(1)}%`;
    const fmtDur = (sec) => {
        const s = Number(sec) || 0;
        if (s < 60) return `${s}s`;
        if (s < 3600) return `${Math.round(s / 60)} min`;
        return `${Math.round(s / 3600)} h`;
    };

    if (loading) {
        return (
            <>
                <div className="page-header"><h1 className="page-title">Analytics IA</h1></div>
                <div className="page-content"><div style={{ padding: '3rem', textAlign: 'center', color: '#94a3b8' }}>Chargement des données...</div></div>
            </>
        );
    }

    const k = data?.kpis || {};
    const growthColor = k.growth_pct >= 0 ? '#16a34a' : '#dc2626';
    const growthSign = k.growth_pct >= 0 ? '+' : '';

    const SEV_BADGES = {
        high:   { bg: '#fee2e2', color: '#991b1b', label: 'PRIORITÉ HAUTE' },
        medium: { bg: '#fef3c7', color: '#92400e', label: 'IMPORTANT' },
        low:    { bg: '#e0f2fe', color: '#075985', label: 'À CONSIDÉRER' },
        info:   { bg: '#f1f5f9', color: '#475569', label: 'INFO' },
    };
    const CAT_BADGES = {
        pricing:    '#6366f1',
        packs:      '#0891b2',
        modules:    '#a855f7',
        conversion: '#f59e0b',
        retention:  '#16a34a',
        churn:      '#dc2626',
        growth:     '#0284c7',
        risk:       '#ea580c',
    };

    const maxRevenue = Math.max(1, ...(data?.revenue_timeseries || []).map(d => Number(d.revenue) || 0));
    const maxBucket = Math.max(1, ...(data?.recharge_distribution || []).map(b => b.count || 0));
    const maxPack = Math.max(1, ...(data?.packs || []).map(p => p.revenue || 0));

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Analytics IA</h1>
                    <p className="page-subtitle">Analyse des recharges, abonnements & modules + idées d'optimisation générées par IA</p>
                </div>
                <div className="page-actions" style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', alignItems: 'center' }}>
                    <select className="form-input" style={{ width: 160 }} value={period} onChange={(e) => setPeriod(parseInt(e.target.value))}>
                        <option value={7}>7 derniers jours</option>
                        <option value={30}>30 derniers jours</option>
                        <option value={90}>90 derniers jours</option>
                        <option value={180}>6 derniers mois</option>
                        <option value={365}>12 derniers mois</option>
                    </select>
                    <button className="btn btn-secondary btn-sm" onClick={() => load(true)} disabled={refreshing}>
                        <Icons.Refresh className="w-4 h-4" /> {refreshing ? 'Sync...' : 'Actualiser'}
                    </button>
                </div>
            </div>

            <div className="page-content">
                {/* KPIs */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '0.75rem', marginBottom: '1.25rem' }}>
                    <StatCard label={`Revenu (${period}j)`} value={fmtCHF(k.total_revenue)} color="#16a34a" icon={<Icons.DollarSign className="w-5 h-5" />} />
                    <StatCard label={`vs période précédente`} value={`${growthSign}${fmtPct(k.growth_pct)}`} color={growthColor} icon={<Icons.TrendingUp className="w-5 h-5" />} />
                    <StatCard label="Panier moyen" value={fmtCHF(k.avg_basket)} color="#0891b2" icon={<Icons.CreditCard className="w-5 h-5" />} />
                    <StatCard label="Conversion checkout" value={fmtPct(k.conversion_rate)} color="#f59e0b" icon={<Icons.Activity className="w-5 h-5" />} />
                    <StatCard label="Clients payants (période)" value={fmtNum(k.paying_clients_period)} color="#6366f1" icon={<Icons.Users className="w-5 h-5" />} />
                    <StatCard label="Repeat purchase" value={fmtPct(k.repeat_purchase_rate_pct)} color="#a855f7" icon={<Icons.Refresh className="w-5 h-5" />} />
                    <StatCard label="MRR (lignes + modules)" value={fmtCHF(k.mrr_lines)} color="#0284c7" icon={<Icons.TrendingUp className="w-5 h-5" />} />
                    <StatCard label="Abonnements actifs" value={fmtNum(k.active_subscriptions)} color="#0d9488" icon={<Icons.CreditCard className="w-5 h-5" />} />
                    <StatCard label="Churn (période)" value={fmtPct(k.churn_rate_pct)} color="#dc2626" icon={<Icons.AlertTriangle className="w-5 h-5" />} />
                    <StatCard label="ARPU (période)" value={fmtCHF(k.arpu_period)} color="#ea580c" icon={<Icons.DollarSign className="w-5 h-5" />} />
                    <StatCard label="Bonus offerts" value={fmtCHF(k.bonus_offered)} color="#0891b2" icon={<Icons.Gift className="w-5 h-5" />} />
                    <StatCard label="Wallet outstanding" value={fmtCHF(k.total_balance_outstanding)} color="#475569" icon={<Icons.CreditCard className="w-5 h-5" />} />
                </div>

                {/* Quick recos (déterministe, gratuit, instantané) */}
                <QuickRecosPanel data={data} />

                {/* Per-client smart offers (rules-based, on-demand load) */}
                <ClientOffersPanel period={period} notify={notify} />

                {/* AI Insights generator */}
                <div className="card" style={{
                    padding: '1.5rem',
                    marginBottom: '1.5rem',
                    background: 'linear-gradient(135deg, #6366f1 0%, #a855f7 100%)',
                    color: '#fff',
                    border: 'none',
                }}>
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: '1rem', flexWrap: 'wrap' }}>
                        <div style={{
                            width: 56, height: 56, borderRadius: 16,
                            background: 'rgba(255,255,255,0.18)',
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            flexShrink: 0,
                        }}>
                            <Icons.Sparkles className="w-7 h-7" />
                        </div>
                        <div style={{ flex: 1, minWidth: 260 }}>
                            <div style={{ fontSize: '1.25rem', fontWeight: 800, marginBottom: '0.35rem' }}>
                                Idées d'amélioration générées par IA
                            </div>
                            <div style={{ fontSize: '0.9rem', opacity: 0.92, marginBottom: '0.85rem', lineHeight: 1.5 }}>
                                gpt-4o-mini analyse vos chiffres réels (recharges, packs, modules, churn, ARPU) et propose des optimisations
                                concrètes : prix, paliers, bundles, promos, lutte contre le churn.
                            </div>
                            <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
                                <input
                                    className="form-input"
                                    style={{ flex: 1, minWidth: 200, background: 'rgba(255,255,255,0.95)', color: '#0f172a', border: 'none' }}
                                    placeholder="Focus optionnel : ex. 'optimiser les packs', 'réduire le churn', 'pricing modules'..."
                                    value={focus}
                                    onChange={(e) => setFocus(e.target.value)}
                                    disabled={insightsLoading}
                                />
                                <button
                                    className="btn btn-sm"
                                    onClick={generateInsights}
                                    disabled={insightsLoading}
                                    style={{ background: '#fff', color: '#6366f1', fontWeight: 700 }}
                                >
                                    <Icons.Lightbulb className="w-4 h-4" />
                                    {insightsLoading ? 'Analyse en cours (15-30s)...' : (insights ? 'Régénérer' : 'Générer mes idées')}
                                </button>
                            </div>
                        </div>
                    </div>

                    {insights && insights.insights && (
                        <div style={{ marginTop: '1.25rem', display: 'grid', gap: '0.75rem' }}>
                            {insights.insights.length === 0 ? (
                                <div style={{ background: 'rgba(255,255,255,0.15)', padding: '1rem', borderRadius: 12 }}>
                                    L'IA n'a retourné aucun insight - réessaie avec un focus différent.
                                </div>
                            ) : insights.insights.map((ins, i) => {
                                const sev = SEV_BADGES[ins.severity] || SEV_BADGES.info;
                                const catColor = CAT_BADGES[ins.category] || '#475569';
                                return (
                                    <div key={i} style={{
                                        background: '#fff',
                                        color: '#0f172a',
                                        borderRadius: 14,
                                        padding: '1rem 1.25rem',
                                        boxShadow: '0 4px 18px rgba(0,0,0,0.12)',
                                    }}>
                                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '0.4rem' }}>
                                            <span style={{
                                                background: sev.bg, color: sev.color,
                                                padding: '0.2rem 0.55rem', borderRadius: 6,
                                                fontSize: '0.65rem', fontWeight: 800, letterSpacing: '0.04em'
                                            }}>{sev.label}</span>
                                            {ins.category && (
                                                <span style={{
                                                    background: `${catColor}1a`, color: catColor,
                                                    padding: '0.2rem 0.55rem', borderRadius: 6,
                                                    fontSize: '0.65rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em',
                                                }}>{ins.category}</span>
                                            )}
                                            {ins.confidence && (
                                                <span style={{ fontSize: '0.7rem', color: '#94a3b8' }}>
                                                    confiance : <strong>{ins.confidence}</strong>
                                                </span>
                                            )}
                                        </div>
                                        <div style={{ fontSize: '1rem', fontWeight: 700, marginBottom: '0.4rem' }}>{ins.title}</div>
                                        {ins.observation && (
                                            <div style={{ fontSize: '0.85rem', color: '#475569', marginBottom: '0.35rem' }}>
                                                <strong style={{ color: '#0f172a' }}>Observation : </strong>{ins.observation}
                                            </div>
                                        )}
                                        {ins.recommendation && (
                                            <div style={{ fontSize: '0.85rem', color: '#475569', marginBottom: '0.35rem' }}>
                                                <strong style={{ color: '#16a34a' }}>Recommandation : </strong>{ins.recommendation}
                                            </div>
                                        )}
                                        {ins.expected_impact && (
                                            <div style={{ fontSize: '0.85rem', color: '#475569' }}>
                                                <strong style={{ color: '#6366f1' }}>Impact attendu : </strong>{ins.expected_impact}
                                            </div>
                                        )}
                                    </div>
                                );
                            })}
                            <div style={{ fontSize: '0.7rem', opacity: 0.85, textAlign: 'right', marginTop: '0.25rem' }}>
                                Généré le {new Date(insights.generated_at).toLocaleString('fr-FR')} - période : {insights.period_days} jours
                            </div>
                        </div>
                    )}
                </div>

                {/* Two-column layout */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(380px, 1fr))', gap: '1rem', marginBottom: '1.25rem' }}>

                    {/* Revenue timeseries */}
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}>
                            <div style={{ fontWeight: 700, fontSize: '0.95rem' }}>Revenu quotidien</div>
                            <div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>{(data?.revenue_timeseries || []).length} jours</div>
                        </div>
                        {(data?.revenue_timeseries || []).length === 0 ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucune transaction sur la période</div>
                        ) : (
                            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 2, height: 160, padding: '0.5rem 0' }}>
                                {(data?.revenue_timeseries || []).map((d, i) => {
                                    const h = Math.max(2, Math.round((Number(d.revenue) || 0) / maxRevenue * 150));
                                    return (
                                        <div key={i} title={`${d.day} - ${fmtCHF(d.revenue)} (${d.succeeded}✓ / ${d.failed}✗)`}
                                            style={{ flex: 1, minWidth: 4 }}>
                                            <div style={{
                                                width: '100%',
                                                height: h,
                                                background: 'linear-gradient(180deg,#6366f1,#a855f7)',
                                                borderRadius: '3px 3px 0 0',
                                            }} />
                                        </div>
                                    );
                                })}
                            </div>
                        )}
                    </div>

                    {/* Pack distribution */}
                    <div className="card" style={{ padding: '1.25rem' }}>
                        <div style={{ fontWeight: 700, fontSize: '0.95rem', marginBottom: '0.85rem' }}>Distribution des montants de recharge</div>
                        {(data?.recharge_distribution || []).every(b => b.count === 0) ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucune recharge</div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
                                {(data?.recharge_distribution || []).map((b, i) => {
                                    const pct = (b.count / maxBucket) * 100;
                                    return (
                                        <div key={i} style={{ display: 'grid', gridTemplateColumns: '70px 1fr 80px 90px', alignItems: 'center', gap: '0.5rem', fontSize: '0.8rem' }}>
                                            <div style={{ color: '#475569', fontFamily: 'ui-monospace' }}>{b.label} CHF</div>
                                            <div style={{ background: '#f1f5f9', borderRadius: 6, height: 18, overflow: 'hidden' }}>
                                                <div style={{ width: `${pct}%`, background: '#6366f1', height: '100%' }} />
                                            </div>
                                            <div style={{ textAlign: 'right', color: '#64748b' }}>{b.count} achats</div>
                                            <div style={{ textAlign: 'right', fontWeight: 600, color: '#0f172a' }}>{fmtCHF(b.revenue)}</div>
                                        </div>
                                    );
                                })}
                            </div>
                        )}
                    </div>
                </div>

                {/* Packs detail */}
                <div id="analytics-anchor-packs" className="card" style={{ marginBottom: '1.25rem', overflow: 'hidden', scrollMarginTop: '1rem' }}>
                    <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e2e8f0', fontWeight: 700, fontSize: '0.95rem' }}>
                        Performance par pack
                    </div>
                    {(data?.packs || []).length === 0 ? (
                        <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucun pack vendu sur la période</div>
                    ) : (
                        <div style={{ overflowX: 'auto' }}>
                            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem' }}>
                                <thead>
                                    <tr style={{ background: '#f8fafc', color: '#475569', fontWeight: 600 }}>
                                        <th style={{ textAlign: 'left', padding: '0.65rem 1rem' }}>Pack</th>
                                        <th style={{ textAlign: 'right', padding: '0.65rem 1rem' }}>Achats</th>
                                        <th style={{ textAlign: 'right', padding: '0.65rem 1rem' }}>Revenu</th>
                                        <th style={{ textAlign: 'right', padding: '0.65rem 1rem' }}>Bonus</th>
                                        <th style={{ textAlign: 'right', padding: '0.65rem 1rem' }}>Panier moy.</th>
                                        <th style={{ padding: '0.65rem 1rem', width: 200 }}>Part</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {(data?.packs || []).map(p => {
                                        const pct = (p.revenue / maxPack) * 100;
                                        return (
                                            <tr key={p.pack_id} style={{ borderTop: '1px solid #f1f5f9' }}>
                                                <td style={{ padding: '0.65rem 1rem', fontFamily: 'ui-monospace', color: '#0f172a' }}>{p.pack_id}</td>
                                                <td style={{ padding: '0.65rem 1rem', textAlign: 'right' }}>{fmtNum(p.count)}</td>
                                                <td style={{ padding: '0.65rem 1rem', textAlign: 'right', fontWeight: 700 }}>{fmtCHF(p.revenue)}</td>
                                                <td style={{ padding: '0.65rem 1rem', textAlign: 'right', color: '#0891b2' }}>{fmtCHF(p.bonus)}</td>
                                                <td style={{ padding: '0.65rem 1rem', textAlign: 'right', color: '#475569' }}>{fmtCHF(p.avg)}</td>
                                                <td style={{ padding: '0.65rem 1rem' }}>
                                                    <div style={{ background: '#f1f5f9', borderRadius: 4, height: 6 }}>
                                                        <div style={{ width: `${pct}%`, background: '#6366f1', height: '100%', borderRadius: 4 }} />
                                                    </div>
                                                </td>
                                            </tr>
                                        );
                                    })}
                                </tbody>
                            </table>
                        </div>
                    )}
                </div>

                {/* Modules + top clients */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(380px, 1fr))', gap: '1rem', marginBottom: '1.25rem' }}>
                    <div id="analytics-anchor-modules" className="card" style={{ overflow: 'hidden', scrollMarginTop: '1rem' }}>
                        <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e2e8f0', fontWeight: 700, fontSize: '0.95rem' }}>
                            Modules - adoption & MRR potentiel
                        </div>
                        {(data?.modules || []).length === 0 ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucun module configuré</div>
                        ) : (
                            <div style={{ overflowX: 'auto' }}>
                                <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.82rem' }}>
                                    <thead>
                                        <tr style={{ background: '#f8fafc', color: '#475569', fontWeight: 600 }}>
                                            <th style={{ textAlign: 'left', padding: '0.55rem 0.8rem' }}>Module</th>
                                            <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Prix</th>
                                            <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Actifs</th>
                                            <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Trial</th>
                                            <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Annulés</th>
                                            <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>MRR pot.</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        {(data?.modules || []).map(m => (
                                            <tr key={m.id} style={{ borderTop: '1px solid #f1f5f9' }}>
                                                <td style={{ padding: '0.55rem 0.8rem' }}>
                                                    <div style={{ fontWeight: 600, color: '#0f172a' }}>{m.name}</div>
                                                    <div style={{ fontSize: '0.7rem', color: '#94a3b8', fontFamily: 'ui-monospace' }}>{m.code}</div>
                                                </td>
                                                <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#475569' }}>{fmtCHF(m.price_chf)}</td>
                                                <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#16a34a', fontWeight: 700 }}>{m.subscribers_active || 0}</td>
                                                <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#f59e0b' }}>{m.subscribers_trial || 0}</td>
                                                <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#dc2626' }}>{m.subscribers_cancelled || 0}</td>
                                                <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', fontWeight: 700, color: '#6366f1' }}>{fmtCHF(m.potential_mrr)}</td>
                                            </tr>
                                        ))}
                                    </tbody>
                                </table>
                            </div>
                        )}
                    </div>

                    <div id="analytics-anchor-topclients" className="card" style={{ overflow: 'hidden', scrollMarginTop: '1rem' }}>
                        <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e2e8f0', fontWeight: 700, fontSize: '0.95rem' }}>
                            Top 10 clients (revenu période)
                        </div>
                        {(data?.top_clients || []).length === 0 ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucun client payant</div>
                        ) : (
                            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem' }}>
                                <tbody>
                                    {(data?.top_clients || []).map((c, i) => (
                                        <tr key={c.id} style={{ borderTop: i === 0 ? 'none' : '1px solid #f1f5f9' }}>
                                            <td style={{ padding: '0.6rem 0.8rem', width: 30, color: '#94a3b8', fontWeight: 700 }}>{i + 1}</td>
                                            <td style={{ padding: '0.6rem 0.8rem' }}>
                                                <div style={{ fontWeight: 600, color: '#0f172a' }}>{c.name || '(sans nom)'}</div>
                                                <div style={{ fontSize: '0.72rem', color: '#94a3b8' }}>{c.email || '-'}</div>
                                            </td>
                                            <td style={{ padding: '0.6rem 0.8rem', textAlign: 'right', color: '#475569', fontSize: '0.78rem' }}>
                                                {c.purchases} achat{c.purchases > 1 ? 's' : ''}
                                            </td>
                                            <td style={{ padding: '0.6rem 0.8rem', textAlign: 'right', fontWeight: 700, color: '#16a34a' }}>{fmtCHF(c.revenue)}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        )}
                    </div>
                </div>

                {/* Subs + Wallet breakdown */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(380px, 1fr))', gap: '1rem', marginBottom: '1.25rem' }}>
                    <div id="analytics-anchor-subs" className="card" style={{ overflow: 'hidden', scrollMarginTop: '1rem' }}>
                        <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e2e8f0', fontWeight: 700, fontSize: '0.95rem' }}>
                            Abonnements lignes
                        </div>
                        {(data?.subs_by_plan || []).length === 0 ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucun abonnement</div>
                        ) : (
                            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem' }}>
                                <thead>
                                    <tr style={{ background: '#f8fafc', color: '#475569', fontWeight: 600 }}>
                                        <th style={{ textAlign: 'left', padding: '0.55rem 0.8rem' }}>Plan</th>
                                        <th style={{ textAlign: 'left', padding: '0.55rem 0.8rem' }}>Période</th>
                                        <th style={{ textAlign: 'left', padding: '0.55rem 0.8rem' }}>Statut</th>
                                        <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Lignes</th>
                                        <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Revenu</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {(data?.subs_by_plan || []).map((s, i) => (
                                        <tr key={i} style={{ borderTop: '1px solid #f1f5f9' }}>
                                            <td style={{ padding: '0.55rem 0.8rem', fontWeight: 600 }}>{s.plan || '(défaut)'}</td>
                                            <td style={{ padding: '0.55rem 0.8rem', color: '#475569' }}>{s.billing_period || '-'}</td>
                                            <td style={{ padding: '0.55rem 0.8rem' }}>
                                                <span style={{
                                                    fontSize: '0.7rem', padding: '0.15rem 0.45rem', borderRadius: 4, fontWeight: 700,
                                                    background: s.status === 'active' ? '#dcfce7' : '#fee2e2',
                                                    color: s.status === 'active' ? '#15803d' : '#991b1b',
                                                }}>{s.status}</span>
                                            </td>
                                            <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', fontWeight: 700 }}>{s.n}</td>
                                            <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#475569' }}>{fmtCHF(s.revenue)}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        )}
                    </div>

                    <div id="analytics-anchor-wallet" className="card" style={{ overflow: 'hidden', scrollMarginTop: '1rem' }}>
                        <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid #e2e8f0', fontWeight: 700, fontSize: '0.95rem' }}>
                            Mouvements wallet ({period}j)
                        </div>
                        {(data?.wallet_breakdown || []).length === 0 ? (
                            <div style={{ padding: '2rem', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>Aucun mouvement</div>
                        ) : (
                            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem' }}>
                                <thead>
                                    <tr style={{ background: '#f8fafc', color: '#475569', fontWeight: 600 }}>
                                        <th style={{ textAlign: 'left', padding: '0.55rem 0.8rem' }}>Type</th>
                                        <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Nombre</th>
                                        <th style={{ textAlign: 'right', padding: '0.55rem 0.8rem' }}>Total</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {(data?.wallet_breakdown || []).map((w, i) => (
                                        <tr key={i} style={{ borderTop: '1px solid #f1f5f9' }}>
                                            <td style={{ padding: '0.55rem 0.8rem', fontFamily: 'ui-monospace', color: '#0f172a' }}>{w.type}</td>
                                            <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', color: '#475569' }}>{fmtNum(w.n)}</td>
                                            <td style={{ padding: '0.55rem 0.8rem', textAlign: 'right', fontWeight: 700, color: w.sum_amount >= 0 ? '#16a34a' : '#dc2626' }}>
                                                {w.sum_amount >= 0 ? '+' : ''}{fmtCHF(w.sum_amount)}
                                            </td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        )}
                    </div>
                </div>

                {/* Calls volume info */}
                <div className="card" style={{ padding: '1rem 1.25rem', display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: '0.5rem', fontSize: '0.85rem', color: '#475569' }}>
                    <span>Sur {period} jours : <strong style={{ color: '#0f172a' }}>{fmtNum(k.calls_count_period)}</strong> appels - durée totale <strong style={{ color: '#0f172a' }}>{fmtDur(k.calls_duration_period_sec)}</strong></span>
                    <span>Coût Twilio estimé : <strong style={{ color: '#0f172a' }}>{fmtCHF(k.calls_cost_period)}</strong></span>
                    <span>All-time rechargé : <strong style={{ color: '#0f172a' }}>{fmtCHF(k.total_recharged_all_time)}</strong> - consommé : <strong style={{ color: '#0f172a' }}>{fmtCHF(k.total_consumed_all_time)}</strong></span>
                </div>

                <div style={{ marginTop: '1rem', fontSize: '0.7rem', color: '#94a3b8', textAlign: 'right' }}>
                    Dernière mise à jour : {data?.generated_at ? new Date(data.generated_at).toLocaleString('fr-FR') : '-'}
                </div>
            </div>
        </>
    );
};

const DiagnosticView = () => {
    const { notify } = useApp();
    const [stats, setStats] = useState(null);
    const [items, setItems] = useState([]);
    const [total, setTotal] = useState(0);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);
    const [autoRefresh, setAutoRefresh] = useState(true);
    const [statusFilter, setStatusFilter] = useState('unread');
    const [severityFilter, setSeverityFilter] = useState('');
    const [sourceFilter, setSourceFilter] = useState('');
    const [search, setSearch] = useState('');
    const [expandedId, setExpandedId] = useState(null);
    const [busy, setBusy] = useState(null); // { id, action }
    const [confirmClear, setConfirmClear] = useState(false);

    const SEVERITY_BADGES = {
        critical: { bg: '#fee2e2', color: '#991b1b', label: 'CRITICAL' },
        error: { bg: '#fef3c7', color: '#92400e', label: 'ERROR' },
        warning: { bg: '#e0f2fe', color: '#075985', label: 'WARNING' },
    };

    const load = async (opts = {}) => {
        if (opts.silent) setRefreshing(true); else setLoading(true);
        try {
            const params = new URLSearchParams();
            params.set('status', statusFilter);
            if (severityFilter) params.set('severity', severityFilter);
            if (sourceFilter) params.set('source', sourceFilter);
            if (search.trim()) params.set('search', search.trim());
            params.set('limit', '200');
            const [errResp, statsResp] = await Promise.all([
                api.get(`/api/admin/diagnostics/errors?${params.toString()}`),
                api.get('/api/admin/diagnostics/stats'),
            ]);
            if (errResp?.error) throw new Error(errResp.error);
            if (statsResp?.error) throw new Error(statsResp.error);
            setItems(errResp.items || []);
            setTotal(errResp.total || 0);
            setStats(statsResp || null);
        } catch (e) {
            notify.error('Erreur chargement diagnostics: ' + e.message);
        } finally {
            setLoading(false);
            setRefreshing(false);
        }
    };

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

    useEffect(() => {
        if (!autoRefresh) return;
        const t = setInterval(() => load({ silent: true }), 15000);
        return () => clearInterval(t);
    }, [autoRefresh, statusFilter, severityFilter, sourceFilter, search]);

    const sources = useMemo(() => {
        const set = new Set(items.map(i => i.source));
        if (stats?.by_source) stats.by_source.forEach(s => set.add(s.source));
        return Array.from(set).sort();
    }, [items, stats]);

    const handleMarkRead = async (id) => {
        setBusy({ id, action: 'read' });
        try {
            const r = await api.post(`/api/admin/diagnostics/errors/${id}/read`);
            if (r?.error) throw new Error(r.error);
            notify.success('Erreur marquée comme résolue');
            await load({ silent: true });
        } catch (e) { notify.error(e.message); }
        finally { setBusy(null); }
    };

    const handleResend = async (id) => {
        setBusy({ id, action: 'notify' });
        try {
            const r = await api.post(`/api/admin/diagnostics/errors/${id}/notify`);
            if (r?.error || r?.success === false) throw new Error(r.error || 'Échec Telegram');
            notify.success('Notification Telegram envoyée');
            await load({ silent: true });
        } catch (e) { notify.error('Telegram: ' + e.message); }
        finally { setBusy(null); }
    };

    const handleDelete = async (id) => {
        if (!confirm('Supprimer définitivement cette erreur ?')) return;
        setBusy({ id, action: 'delete' });
        try {
            const r = await api.del(`/api/admin/diagnostics/errors/${id}`);
            if (r?.error) throw new Error(r.error);
            notify.success('Erreur supprimée');
            await load({ silent: true });
        } catch (e) { notify.error(e.message); }
        finally { setBusy(null); }
    };

    const handleMarkAll = async () => {
        if (!confirm(`Marquer comme résolues TOUTES les erreurs non lues (${stats?.counts?.unread || 0}) ?`)) return;
        try {
            const r = await api.post('/api/admin/diagnostics/errors/mark-all-read');
            if (r?.error) throw new Error(r.error);
            notify.success(`${r.updated || 0} erreur(s) marquée(s) résolue(s)`);
            await load({ silent: true });
        } catch (e) { notify.error(e.message); }
    };

    const handleClear = async (scope) => {
        try {
            const r = await api.post(`/api/admin/diagnostics/errors/clear?scope=${scope}`);
            if (r?.error) throw new Error(r.error);
            notify.success(`${r.deleted || 0} erreur(s) supprimée(s)`);
            setConfirmClear(false);
            await load({ silent: true });
        } catch (e) { notify.error(e.message); }
    };

    const handleProcess = async () => {
        try {
            const r = await api.post('/api/admin/diagnostics/process');
            if (r?.error) throw new Error(r.error);
            notify.success(`Cron lancé: ${r.sent || 0} envoyé(s), ${r.skipped || 0} échec(s)`);
            await load({ silent: true });
        } catch (e) { notify.error(e.message); }
    };

    const handleTest = async () => {
        try {
            const r = await api.post('/api/admin/diagnostics/test-telegram');
            if (r?.error || r?.success === false) throw new Error(r.error || 'Échec Telegram');
            notify.success('Test Telegram envoyé !');
        } catch (e) { notify.error('Telegram: ' + e.message); }
    };

    const formatRelative = (ts) => {
        if (!ts) return '';
        const d = new Date(ts.replace(' ', 'T') + 'Z');
        const diff = Math.floor((Date.now() - d.getTime()) / 1000);
        if (diff < 60) return `il y a ${diff}s`;
        if (diff < 3600) return `il y a ${Math.floor(diff / 60)}min`;
        if (diff < 86400) return `il y a ${Math.floor(diff / 3600)}h`;
        return d.toLocaleString('fr-FR');
    };

    const c = stats?.counts || {};
    const telegramOK = stats?.telegram_configured;

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Diagnostic</h1>
                    <p className="page-subtitle">Erreurs serveur (runtime, D1, Twilio, OpenAI, R2, mail, cron) + alertes Telegram</p>
                </div>
                <div className="page-actions" style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
                    <label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', fontSize: '0.85rem', color: '#475569' }}>
                        <input type="checkbox" checked={autoRefresh} onChange={(e) => setAutoRefresh(e.target.checked)} />
                        Auto-refresh 15s
                    </label>
                    <button className="btn btn-secondary btn-sm" onClick={() => load({ silent: true })} disabled={refreshing}>
                        <Icons.Refresh className="w-4 h-4" /> {refreshing ? 'Actualisation...' : 'Actualiser'}
                    </button>
                    <button className="btn btn-secondary btn-sm" onClick={handleTest} disabled={!telegramOK} title={telegramOK ? '' : 'Telegram non configuré'}>
                        <Icons.Bell className="w-4 h-4" /> Test Telegram
                    </button>
                    <button className="btn btn-primary btn-sm" onClick={handleProcess} disabled={!telegramOK || (c.unnotified || 0) === 0}>
                        <Icons.Send className="w-4 h-4" /> Envoyer alertes ({c.unnotified || 0})
                    </button>
                </div>
            </div>

            <div className="page-content">
                {!telegramOK && (
                    <div style={{ background: '#fef3c7', border: '1px solid #fde68a', color: '#92400e', borderRadius: 8, padding: '0.75rem 1rem', marginBottom: '1rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <Icons.AlertTriangle className="w-4 h-4" />
                        Telegram n'est pas configuré (TELEGRAM_API_URL / TELEGRAM_API_KEY / TELEGRAM_USER_ID manquants dans wrangler.toml).
                    </div>
                )}

                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: '0.75rem', marginBottom: '1.25rem' }}>
                    <StatCard label="Non lues" value={c.unread || 0} color="#dc2626" icon={<Icons.AlertTriangle className="w-5 h-5" />} />
                    <StatCard label="Critical (non résolu)" value={c.critical_unread || 0} color="#991b1b" icon={<Icons.AlertTriangle className="w-5 h-5" />} />
                    <StatCard label="Dernière heure" value={c.last_1h || 0} color="#ea580c" icon={<Icons.Refresh className="w-5 h-5" />} />
                    <StatCard label="24 dernières heures" value={c.last_24h || 0} color="#d97706" icon={<Icons.Refresh className="w-5 h-5" />} />
                    <StatCard label="En attente Telegram" value={c.unnotified || 0} color="#0284c7" icon={<Icons.Send className="w-5 h-5" />} />
                    <StatCard label="Total enregistrées" value={c.total || 0} color="#475569" icon={<Icons.BarChart className="w-5 h-5" />} />
                </div>

                <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', alignItems: 'center', marginBottom: '1rem' }}>
                    <select className="form-input" style={{ width: 160 }} value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
                        <option value="unread">Non lues</option>
                        <option value="resolved">Résolues</option>
                        <option value="all">Toutes</option>
                    </select>
                    <select className="form-input" style={{ width: 140 }} value={severityFilter} onChange={(e) => setSeverityFilter(e.target.value)}>
                        <option value="">Toute sévérité</option>
                        <option value="critical">Critical</option>
                        <option value="error">Error</option>
                        <option value="warning">Warning</option>
                    </select>
                    <select className="form-input" style={{ width: 160 }} value={sourceFilter} onChange={(e) => setSourceFilter(e.target.value)}>
                        <option value="">Toutes sources</option>
                        {sources.map(s => <option key={s} value={s}>{s}</option>)}
                    </select>
                    <input className="form-input" style={{ flex: 1, minWidth: 200 }} placeholder="Rechercher (message ou route)..."
                        value={search} onChange={(e) => setSearch(e.target.value)}
                        onKeyDown={(e) => e.key === 'Enter' && load()} />
                    <button className="btn btn-secondary btn-sm" onClick={() => load()}>Filtrer</button>
                    <button className="btn btn-secondary btn-sm" onClick={handleMarkAll} disabled={(c.unread || 0) === 0}>
                        <Icons.Check className="w-4 h-4" /> Tout marquer résolu
                    </button>
                    <button className="btn btn-danger btn-sm" onClick={() => setConfirmClear(true)}>
                        <Icons.Trash className="w-4 h-4" /> Purger
                    </button>
                </div>

                {loading ? (
                    <div style={{ padding: '3rem', textAlign: 'center', color: '#94a3b8' }}>Chargement...</div>
                ) : items.length === 0 ? (
                    <div className="card" style={{ padding: '4rem 2rem', textAlign: 'center', color: '#94a3b8' }}>
                        <Icons.Check className="w-12 h-12" style={{ margin: '0 auto 1rem', display: 'block', color: '#22c55e' }} />
                        Aucune erreur {statusFilter === 'unread' ? 'non résolue' : ''} pour les filtres sélectionnés.
                    </div>
                ) : (
                    <div className="card" style={{ overflow: 'hidden' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0.75rem 1rem', background: '#f8fafc', borderBottom: '1px solid #e2e8f0', fontSize: '0.8rem', color: '#475569' }}>
                            <span>{items.length} affichée(s) sur {total}</span>
                            <span style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', color: refreshing ? '#0284c7' : '#94a3b8' }}>
                                <Icons.Refresh className="w-3 h-3" /> {refreshing ? 'sync...' : 'à jour'}
                            </span>
                        </div>
                        {items.map(err => {
                            const sev = SEVERITY_BADGES[err.severity] || SEVERITY_BADGES.error;
                            const isExpanded = expandedId === err.id;
                            return (
                                <div key={err.id} style={{ borderBottom: '1px solid #f1f5f9' }}>
                                    <div onClick={() => setExpandedId(isExpanded ? null : err.id)}
                                        style={{
                                            padding: '0.85rem 1rem',
                                            display: 'grid',
                                            gridTemplateColumns: 'auto 1fr auto auto',
                                            gap: '0.85rem',
                                            alignItems: 'center',
                                            cursor: 'pointer',
                                            background: err.is_read ? '#fafbfc' : '#fff',
                                            opacity: err.is_read ? 0.65 : 1,
                                        }}>
                                        <div style={{
                                            width: 32, height: 32, borderRadius: 8,
                                            background: sev.bg, color: sev.color,
                                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                                            flexShrink: 0
                                        }}>
                                            <Icons.AlertTriangle className="w-4 h-4" />
                                        </div>
                                        <div style={{ minWidth: 0 }}>
                                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>
                                                <span style={{ background: sev.bg, color: sev.color, padding: '0.15rem 0.5rem', borderRadius: 4, fontSize: '0.7rem', fontWeight: 700, letterSpacing: '0.03em' }}>{sev.label}</span>
                                                <span style={{ background: '#eef2ff', color: '#4338ca', padding: '0.15rem 0.5rem', borderRadius: 4, fontSize: '0.7rem', fontWeight: 600 }}>{err.source}</span>
                                                {err.http_status ? <span style={{ background: '#fee2e2', color: '#991b1b', padding: '0.15rem 0.5rem', borderRadius: 4, fontSize: '0.7rem', fontWeight: 600 }}>HTTP {err.http_status}</span> : null}
                                                {(err.occurrences || 1) > 1 ? <span style={{ background: '#fff7ed', color: '#c2410c', padding: '0.15rem 0.5rem', borderRadius: 4, fontSize: '0.7rem', fontWeight: 700 }}>×{err.occurrences}</span> : null}
                                                {err.is_notified ? <span title="Telegram envoyé" style={{ color: '#16a34a', fontSize: '0.75rem', display: 'inline-flex', alignItems: 'center', gap: 2 }}><Icons.Bell className="w-3 h-3" /> envoyée</span> : <span style={{ color: '#94a3b8', fontSize: '0.75rem' }}>en attente</span>}
                                            </div>
                                            <div style={{ marginTop: '0.25rem', color: '#0f172a', fontWeight: 500, fontSize: '0.9rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                                {err.message}
                                            </div>
                                            {(err.request_method || err.request_path) && (
                                                <div style={{ color: '#64748b', fontSize: '0.75rem', marginTop: '0.15rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                                    <code>{err.request_method || ''} {err.request_path || ''}</code>
                                                </div>
                                            )}
                                        </div>
                                        <div style={{ color: '#64748b', fontSize: '0.78rem', textAlign: 'right', whiteSpace: 'nowrap' }}>
                                            {formatRelative(err.last_seen_at)}
                                        </div>
                                        <div style={{ color: '#cbd5e1', fontSize: '0.85rem' }}>{isExpanded ? '▼' : '▶'}</div>
                                    </div>
                                    {isExpanded && (
                                        <div style={{ background: '#f8fafc', padding: '1rem 1.25rem', borderTop: '1px solid #e2e8f0' }}>
                                            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '0.75rem', marginBottom: '0.75rem', fontSize: '0.8rem' }}>
                                                <Detail label="ID" value={`#${err.id}`} />
                                                <Detail label="Première vue" value={err.first_seen_at} />
                                                <Detail label="Dernière vue" value={err.last_seen_at} />
                                                <Detail label="Occurrences" value={err.occurrences} />
                                                <Detail label="Fingerprint" value={err.fingerprint} mono />
                                                {err.user_id ? <Detail label="User" value={`#${err.user_id}`} /> : null}
                                                {err.client_id ? <Detail label="Client" value={`#${err.client_id}`} /> : null}
                                                <Detail label="Notifiée" value={err.is_notified ? `Oui (${err.notified_at || ''})` : 'Non'} />
                                            </div>
                                            {err.stack && (
                                                <div style={{ marginBottom: '0.75rem' }}>
                                                    <div style={{ fontSize: '0.75rem', fontWeight: 700, color: '#475569', marginBottom: '0.25rem' }}>STACK</div>
                                                    <pre style={{ margin: 0, padding: '0.75rem', background: '#0f172a', color: '#e2e8f0', borderRadius: 6, fontSize: '0.72rem', overflowX: 'auto', maxHeight: 280 }}>{err.stack}</pre>
                                                </div>
                                            )}
                                            {err.extra && (
                                                <div style={{ marginBottom: '0.75rem' }}>
                                                    <div style={{ fontSize: '0.75rem', fontWeight: 700, color: '#475569', marginBottom: '0.25rem' }}>CONTEXTE</div>
                                                    <pre style={{ margin: 0, padding: '0.75rem', background: '#1e293b', color: '#e2e8f0', borderRadius: 6, fontSize: '0.72rem', overflowX: 'auto', maxHeight: 200 }}>{err.extra}</pre>
                                                </div>
                                            )}
                                            <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
                                                {!err.is_read && (
                                                    <button className="btn btn-success btn-sm" onClick={() => handleMarkRead(err.id)} disabled={busy?.id === err.id}>
                                                        <Icons.Check className="w-4 h-4" /> Marquer résolu
                                                    </button>
                                                )}
                                                <button className="btn btn-secondary btn-sm" onClick={() => handleResend(err.id)} disabled={busy?.id === err.id || !telegramOK}>
                                                    <Icons.Send className="w-4 h-4" /> Renvoyer Telegram
                                                </button>
                                                <button className="btn btn-danger btn-sm" onClick={() => handleDelete(err.id)} disabled={busy?.id === err.id}>
                                                    <Icons.Trash className="w-4 h-4" /> Supprimer
                                                </button>
                                            </div>
                                        </div>
                                    )}
                                </div>
                            );
                        })}
                    </div>
                )}
            </div>

            {confirmClear && (
                <div className="modal-overlay" onClick={() => setConfirmClear(false)}>
                    <div className="modal-content" onClick={e => e.stopPropagation()} style={{ maxWidth: 460 }}>
                        <div className="modal-header">
                            <h2 className="modal-title">Purger les erreurs</h2>
                            <button className="modal-close" onClick={() => setConfirmClear(false)}>&times;</button>
                        </div>
                        <div className="modal-body">
                            <p style={{ color: '#475569', fontSize: '0.9rem' }}>
                                Cette action supprime définitivement les entrées choisies. Aucune notification ne sera renvoyée pour ces erreurs.
                            </p>
                        </div>
                        <div className="modal-footer" style={{ display: 'flex', gap: '0.5rem', justifyContent: 'flex-end' }}>
                            <button className="btn btn-secondary" onClick={() => setConfirmClear(false)}>Annuler</button>
                            <button className="btn btn-secondary" onClick={() => handleClear('resolved')}>Supprimer les résolues</button>
                            <button className="btn btn-danger" onClick={() => handleClear('all')}>Tout supprimer</button>
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

const StatCard = ({ label, value, color, icon }) => (
    <div className="card" style={{ padding: '0.85rem 1rem', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
        <div style={{ width: 36, height: 36, borderRadius: 10, background: `${color}1a`, color: color, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{icon}</div>
        <div style={{ minWidth: 0 }}>
            <div style={{ fontSize: '1.4rem', fontWeight: 800, color: '#0f172a', lineHeight: 1 }}>{value}</div>
            <div style={{ fontSize: '0.72rem', color: '#64748b', marginTop: '0.2rem' }}>{label}</div>
        </div>
    </div>
);

const Detail = ({ label, value, mono }) => (
    <div>
        <div style={{ fontSize: '0.7rem', fontWeight: 700, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: '0.15rem' }}>{label}</div>
        <div style={{ fontSize: '0.85rem', color: '#0f172a', wordBreak: 'break-all', fontFamily: mono ? 'ui-monospace, SFMono-Regular, monospace' : 'inherit' }}>{value || '-'}</div>
    </div>
);

// ==================== SUPPORT VIEW ====================
const SupportView = () => {
    const [activeTab, setActiveTab] = useState('open');

    const tabs = [
        { id: 'open', label: 'Tickets Ouverts' },
        { id: 'resolved', label: 'Résolus' },
        { id: 'all', label: 'Tous' },
    ];

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Support</h1>
                    <p className="page-subtitle">Gestion des tickets de support</p>
                </div>
            </div>
            <div className="page-content">
                <div className="tabs" style={{ marginBottom: '1.5rem' }}>
                    {tabs.map(t => (
                        <button key={t.id} className={`tab ${activeTab === t.id ? 'active' : ''}`} onClick={() => setActiveTab(t.id)}>
                            {t.label}
                        </button>
                    ))}
                </div>

                <div className="empty-state">
                    <Icons.HeadphonesIcon className="empty-state-icon" />
                    <h3>Aucun ticket</h3>
                    <p>Les tickets de support apparaîtront ici.</p>
                </div>
            </div>
        </>
    );
};

// ==================== MOBILE HEADER ====================
const MobileHeader = () => {
    const { setSidebarOpen } = useApp();

    return (
        <div className="mobile-header" style={{
            display: 'none', position: 'sticky', top: 0, zIndex: 50,
            height: 'var(--header-height)', padding: '0 1rem',
            background: 'var(--bg-white)', borderBottom: '1px solid var(--border)',
            alignItems: 'center', gap: '0.75rem',
        }}>
            <button onClick={() => setSidebarOpen(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '0.5rem' }}>
                <Icons.Menu className="w-6 h-6" />
            </button>
            <img src="assets/img/vocal-logotype.svg" alt="Vocal" style={{ height: '28px' }} />
            <span style={{ fontWeight: 700, fontSize: '1rem' }}>Admin</span>
        </div>
    );
};

// ==================== CALLS ====================
const CALL_STATUS = {
    completed: { label: 'Terminé', color: 'badge-success', icon: 'check' },
    ringing: { label: 'Sonne', color: 'badge-info', icon: 'ring' },
    'in-progress': { label: 'En cours', color: 'badge-info', icon: 'ring' },
    busy: { label: 'Occupé', color: 'badge-warning', icon: 'x' },
    'no-answer': { label: 'Sans réponse', color: 'badge-warning', icon: 'missed' },
    failed: { label: 'Échoué', color: 'badge-danger', icon: 'x' },
    canceled: { label: 'Annulé', color: 'badge-gray', icon: 'x' },
    missed: { label: 'Manqué', color: 'badge-danger', icon: 'missed' },
};

const USD_TO_CHF_DISPLAY = 0.90;

// ─────────────────────────────────────────────────────────────────
// Analyse Bot : timeline complète d'un appel Voice AI (events + transcripts
// + captures + config de la ligne au moment de l'appel). Ouvert depuis le
// détail d'un appel via un bouton dans CallCostBreakdown.
// ─────────────────────────────────────────────────────────────────

const BOT_EVENT_META = {
    start:              { color: '#10b981', icon: '▶', label: 'Démarrage' },
    stop:               { color: '#64748b', icon: '■', label: 'Fin d\'appel' },
    dg_settings:        { color: '#6366f1', icon: '⚙', label: 'Settings DG' },
    dg_settings_applied:{ color: '#6366f1', icon: '✓', label: 'Settings appliqués' },
    dg_welcome:         { color: '#6366f1', icon: '👋', label: 'DG Welcome' },
    dg_ack:             { color: '#94a3b8', icon: '·', label: 'DG ACK' },
    dg_event:           { color: '#94a3b8', icon: '·', label: 'DG event' },
    dg_error:           { color: '#ef4444', icon: '!', label: 'DG ERROR' },
    voice_switch:       { color: '#f59e0b', icon: '🔄', label: 'Switch voix' },
    twilio_clear:       { color: '#0ea5e9', icon: '✂', label: 'Barge-in' },
    reprompt:           { color: '#f59e0b', icon: '↻', label: 'Re-speak' },
    function_call:      { color: '#8b5cf6', icon: '𝑓', label: 'Function call' },
    function_response:  { color: '#8b5cf6', icon: '↩', label: 'Function réponse' },
    capture_saved:      { color: '#22c55e', icon: '💾', label: 'Capture' },
    message_saved:      { color: '#22c55e', icon: '💬', label: 'Message' },
    email_sent:         { color: '#0ea5e9', icon: '✉', label: 'Email envoyé' },
    email_failed:       { color: '#ef4444', icon: '✉', label: 'Email échoué' },
};
function getBotEventMeta(type) { return BOT_EVENT_META[type] || { color: '#64748b', icon: '·', label: type }; }

function BotAnalysisModal({ callSid, onClose }) {
    const [data, setData] = React.useState(null);
    const [loading, setLoading] = React.useState(true);
    const [error, setError] = React.useState(null);
    const [showRaw, setShowRaw] = React.useState({}); // id → bool

    React.useEffect(() => {
        let cancelled = false;
        setLoading(true);
        api.get(`/api/voice-ai/calls/${encodeURIComponent(callSid)}/timeline`)
           .then(r => {
               if (cancelled) return;
               // L'API renvoie { error: '...' } en cas de souci HTTP — on l'attrape.
               if (r && r.error) {
                   setError(`API: ${r.error}`);
                   setData(null);
               } else {
                   setData(r);
               }
               setLoading(false);
           })
           .catch(e => { if (!cancelled) { setError(e.message || 'Erreur'); setLoading(false); } });
        return () => { cancelled = true; };
    }, [callSid]);

    // Merge events + transcripts en une seule timeline triée par ts_ms.
    // Pour chaque transcript Agent, on rattache la voix Aura active à ce
    // moment-là, déduite des events lang_warmup_lock / lang_switch_confirmed
    // antérieurs (payload.voice). Tombe en fallback sur data.line.ai_voice.
    const timeline = React.useMemo(() => {
        if (!data) return [];
        const items = [];

        // Pré-calcul : liste ordonnée des changements de voix.
        const voiceChanges = [];
        const initialVoice = data.line?.ai_voice || data.call?.voice || null;
        if (initialVoice) voiceChanges.push({ ts_ms: 0, voice: initialVoice });
        for (const e of (data.events || [])) {
            if (e.type === 'lang_warmup_lock' || e.type === 'lang_switch_confirmed') {
                let v = null;
                try {
                    const p = typeof e.payload === 'string' ? JSON.parse(e.payload) : (e.payload || {});
                    v = p.voice || p.to_voice || null;
                } catch (_) {}
                if (v) voiceChanges.push({ ts_ms: e.ts_ms || 0, voice: v });
            }
        }
        voiceChanges.sort((a, b) => a.ts_ms - b.ts_ms);
        const voiceAt = (ts) => {
            let cur = initialVoice;
            for (const c of voiceChanges) {
                if (c.ts_ms <= ts) cur = c.voice; else break;
            }
            return cur;
        };

        for (const e of (data.events || [])) {
            items.push({ kind: 'event', ts_ms: e.ts_ms || 0, raw: e });
        }
        for (const t of (data.transcripts || [])) {
            const isAgent = t.speaker !== 'caller' && t.speaker !== 'user';
            items.push({
                kind: 'transcript',
                ts_ms: t.ts_ms || 0,
                raw: t,
                voice: isAgent ? voiceAt(t.ts_ms || 0) : null,
            });
        }
        items.sort((a, b) => a.ts_ms - b.ts_ms);
        return items;
    }, [data]);

    // Libellé court d'une voix Aura-2, ex. "aura-2-celeste-es" → "Celeste (es)"
    const shortVoice = (id) => {
        if (!id) return null;
        const m = /^aura-2-([a-z0-9]+)-([a-z]{2})$/i.exec(id);
        if (!m) return id;
        const name = m[1].charAt(0).toUpperCase() + m[1].slice(1);
        return `${name} · ${m[2]}`;
    };

    const fmtMs = (ms) => {
        const s = Math.floor((ms || 0) / 1000);
        const m = Math.floor(s / 60);
        const sec = s % 60;
        const cs = Math.floor(((ms || 0) % 1000) / 10);
        return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}.${String(cs).padStart(2, '0')}`;
    };

    const parsePayload = (p) => {
        if (!p) return null;
        if (typeof p !== 'string') return p;
        try { return JSON.parse(p); } catch { return p; }
    };

    return (
        <div onClick={onClose} style={{
            position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.55)', zIndex: 9999,
            display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '2rem',
        }}>
            <div onClick={e => e.stopPropagation()} style={{
                background: '#fff', borderRadius: 12, maxWidth: 1100, width: '100%', maxHeight: '90vh',
                display: 'flex', flexDirection: 'column', overflow: 'hidden', boxShadow: '0 25px 50px -12px rgba(0,0,0,0.25)',
            }}>
                <div style={{ padding: '1rem 1.5rem', borderBottom: '1px solid #e2e8f0', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                    <div>
                        <div style={{ fontWeight: 700, fontSize: '1rem' }}>Analyse Bot — Timeline d'appel</div>
                        <div style={{ fontSize: '0.72rem', color: '#64748b', fontFamily: 'ui-monospace, monospace', marginTop: 2 }}>{callSid}</div>
                    </div>
                    <button className="btn btn-secondary btn-sm" onClick={onClose}>Fermer</button>
                </div>

                <div style={{ flex: 1, overflow: 'auto', padding: '1rem 1.5rem' }}>
                    {loading && <div style={{ padding: '2rem', textAlign: 'center', color: '#64748b' }}>Chargement…</div>}
                    {error && <div style={{ padding: '2rem', color: '#ef4444' }}>Erreur : {error}</div>}
                    {data && (
                        <>
                            {/* Bloc résumé / config */}
                            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: '0.75rem', marginBottom: '1.25rem' }}>
                                <div style={{ background: '#f8fafc', padding: '0.75rem', borderRadius: 8 }}>
                                    <div style={{ fontSize: '0.62rem', textTransform: 'uppercase', color: '#94a3b8', fontWeight: 700 }}>Ligne</div>
                                    <div style={{ fontWeight: 700, marginTop: 4 }}>{data.line?.label || `#${data.line?.id || '?'}`}</div>
                                    <div style={{ fontSize: '0.78rem', fontFamily: 'ui-monospace, monospace', color: '#64748b' }}>{data.line?.phone_number}</div>
                                </div>
                                <div style={{ background: '#f8fafc', padding: '0.75rem', borderRadius: 8 }}>
                                    <div style={{ fontSize: '0.62rem', textTransform: 'uppercase', color: '#94a3b8', fontWeight: 700 }}>Voix initiale</div>
                                    <div style={{ fontWeight: 700, marginTop: 4 }}>{data.line?.ai_voice || data.call?.voice || '—'}</div>
                                    <div style={{ fontSize: '0.78rem', color: '#64748b' }}>Mode : {data.call?.ai_mode || '—'}</div>
                                </div>
                                <div style={{ background: '#f8fafc', padding: '0.75rem', borderRadius: 8 }}>
                                    <div style={{ fontSize: '0.62rem', textTransform: 'uppercase', color: '#94a3b8', fontWeight: 700 }}>Durée / tours</div>
                                    <div style={{ fontWeight: 700, marginTop: 4 }}>{Math.round((data.call?.duration_ms || 0) / 1000)}s</div>
                                    <div style={{ fontSize: '0.78rem', color: '#64748b' }}>{data.call?.turns_user || 0} user / {data.call?.turns_agent || 0} agent</div>
                                </div>
                                <div style={{ background: '#f8fafc', padding: '0.75rem', borderRadius: 8 }}>
                                    <div style={{ fontSize: '0.62rem', textTransform: 'uppercase', color: '#94a3b8', fontWeight: 700 }}>Capture mode</div>
                                    <div style={{ fontWeight: 700, marginTop: 4 }}>{data.line?.ai_capture_mode || '—'}</div>
                                    <div style={{ fontSize: '0.78rem', color: '#64748b' }}>→ {data.line?.ai_capture_email || '—'}</div>
                                </div>
                            </div>

                            {/* Knowledge + prompt (collapsibles) */}
                            {(data.line?.ai_knowledge || data.line?.voice_ai_prompt) && (
                                <details style={{ marginBottom: '1rem' }}>
                                    <summary style={{ cursor: 'pointer', fontSize: '0.85rem', fontWeight: 600, padding: '0.5rem 0.75rem', background: '#f1f5f9', borderRadius: 6 }}>
                                        Prompt / Connaissance utilisés
                                    </summary>
                                    <div style={{ padding: '0.75rem', background: '#fafafa', borderRadius: 6, marginTop: 6, fontSize: '0.78rem', whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace', maxHeight: 280, overflow: 'auto' }}>
                                        {data.line?.voice_ai_prompt && <><b>PROMPT :</b>{'\n'}{data.line.voice_ai_prompt}{'\n\n'}</>}
                                        {data.line?.ai_knowledge && <><b>KNOWLEDGE :</b>{'\n'}{data.line.ai_knowledge}</>}
                                    </div>
                                </details>
                            )}

                            {/* Captures / messages */}
                            {(data.captures || []).length > 0 && (
                                <div style={{ marginBottom: '1rem' }}>
                                    <div style={{ fontSize: '0.78rem', fontWeight: 700, marginBottom: 6 }}>Données capturées</div>
                                    {data.captures.map(c => (
                                        <div key={c.id} style={{ padding: '0.6rem 0.85rem', background: '#ecfdf5', border: '1px solid #d1fae5', borderRadius: 8, marginBottom: 6 }}>
                                            <div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#15803d', textTransform: 'uppercase' }}>{c.mode}</div>
                                            <pre style={{ margin: '4px 0 0', fontSize: '0.75rem', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
                                                {JSON.stringify(parsePayload(c.payload), null, 2)}
                                            </pre>
                                            <div style={{ fontSize: '0.68rem', color: '#64748b', marginTop: 4 }}>
                                                {c.email_to ? `→ ${c.email_to}` : 'aucun email destinataire'}{' · '}
                                                {c.email_sent_at ? `envoyé à ${c.email_sent_at}` : (c.email_error ? `échec : ${c.email_error}` : 'pas encore envoyé')}
                                            </div>
                                        </div>
                                    ))}
                                </div>
                            )}

                            {/* Timeline interleavée */}
                            <div style={{ fontSize: '0.78rem', fontWeight: 700, marginBottom: 6 }}>
                                Timeline ({timeline.length} entrées : {data.events?.length || 0} events + {data.transcripts?.length || 0} tours)
                            </div>
                            <div style={{ borderLeft: '2px solid #e2e8f0', paddingLeft: '0.75rem' }}>
                                {timeline.map((item, i) => {
                                    if (item.kind === 'transcript') {
                                        const t = item.raw;
                                        const isUser = t.speaker === 'caller' || t.speaker === 'user';
                                        return (
                                            <div key={`t${t.id}`} style={{ padding: '0.5rem 0.75rem', margin: '0.3rem 0', background: isUser ? '#f0f9ff' : '#fef3c7', borderRadius: 6, position: 'relative' }}>
                                                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.62rem', color: '#64748b', marginBottom: 2 }}>
                                                    <span style={{ fontWeight: 700, color: isUser ? '#0369a1' : '#a16207' }}>
                                                        {isUser ? '👤 Appelant' : '🤖 Agent'} {t.language ? `[${t.language}]` : ''}
                                                        {!isUser && item.voice && (
                                                            <span style={{ marginLeft: 6, fontWeight: 500, color: '#92400e', background: '#fde68a', padding: '1px 6px', borderRadius: 4, fontSize: '0.62rem' }}>
                                                                🔊 {shortVoice(item.voice)}
                                                            </span>
                                                        )}
                                                    </span>
                                                    <span style={{ fontFamily: 'ui-monospace, monospace' }}>{fmtMs(t.ts_ms)}</span>
                                                </div>
                                                <div style={{ fontSize: '0.82rem' }}>{t.transcript}</div>
                                            </div>
                                        );
                                    }
                                    // event
                                    const e = item.raw;
                                    const meta = getBotEventMeta(e.type);
                                    const payload = parsePayload(e.payload);
                                    const isShown = showRaw[e.id];
                                    return (
                                        <div key={`e${e.id}`} style={{ padding: '0.35rem 0.75rem', margin: '0.15rem 0', display: 'flex', gap: '0.5rem', fontSize: '0.75rem', alignItems: 'baseline' }}>
                                            <span style={{ fontFamily: 'ui-monospace, monospace', color: '#94a3b8', fontSize: '0.68rem', minWidth: 64 }}>{fmtMs(e.ts_ms)}</span>
                                            <span style={{ color: meta.color, fontWeight: 700, minWidth: 16, textAlign: 'center' }}>{meta.icon}</span>
                                            <span style={{ color: e.level === 'error' ? '#ef4444' : (e.level === 'warn' ? '#f59e0b' : '#475569'), fontWeight: 600, minWidth: 130, fontSize: '0.7rem', textTransform: 'uppercase' }}>{meta.label}</span>
                                            <div style={{ flex: 1 }}>
                                                <div style={{ color: e.level === 'error' ? '#ef4444' : '#0f172a' }}>{e.message || '—'}</div>
                                                {payload && (
                                                    <div style={{ marginTop: 2 }}>
                                                        <button onClick={() => setShowRaw(s => ({ ...s, [e.id]: !s[e.id] }))} style={{ background: 'none', border: 'none', color: '#6366f1', fontSize: '0.65rem', padding: 0, cursor: 'pointer' }}>
                                                            {isShown ? '− cacher' : '+ payload'}
                                                        </button>
                                                        {isShown && (
                                                            <pre style={{ margin: '4px 0 0', fontSize: '0.7rem', background: '#f8fafc', padding: '0.5rem', borderRadius: 4, whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 180, overflow: 'auto' }}>
                                                                {typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2)}
                                                            </pre>
                                                        )}
                                                    </div>
                                                )}
                                            </div>
                                        </div>
                                    );
                                })}
                                {timeline.length === 0 && <div style={{ color: '#94a3b8', fontSize: '0.85rem', padding: '1rem' }}>Aucun event ni transcript pour cet appel.</div>}
                            </div>
                        </>
                    )}
                </div>
            </div>
        </div>
    );
}

function CallCostBreakdown({ call }) {
    if (!call) return null;
    const billedTotal = Number(call.billed_price) || 0;

    // Fetch infos Voice Agent IA (si applicable) — async.
    const [aiCall, setAiCall] = React.useState(null);
    const [showBotAnalysis, setShowBotAnalysis] = React.useState(false);
    React.useEffect(() => {
        let cancelled = false;
        if (!call.call_sid) { setAiCall(null); return; }
        api.get(`/api/voice-ai/calls/${encodeURIComponent(call.call_sid)}`)
           .then(r => { if (!cancelled && r && r.call) setAiCall(r.call); })
           .catch(() => {});
        return () => { cancelled = true; };
    }, [call.call_sid]);

    if (!call.is_price_synced && billedTotal === 0 && !aiCall && !call.call_sid) return null;

    const billedVoice = Number(call.billed_voice_price) || 0;
    const billedRec = Number(call.billed_recording_price) || 0;
    const billedTr = Number(call.billed_transcription_price) || 0;

    const twilioVoiceUsd = Number(call.price) || 0;
    const twilioRecUsd = Number(call.recording_price_twilio) || 0;
    const trCostChf = Number(call.transcription_cost_chf) || 0;

    // Coûts IA en USD -> CHF pour homogénéité.
    // Préférer le coût RÉEL facturé par Deepgram (cost_billed_usd, rempli par
    // le cron de réconciliation), sinon retomber sur l'estimation locale.
    const aiBilledUsd = aiCall && aiCall.cost_billed_usd != null ? Number(aiCall.cost_billed_usd) || 0 : null;
    const aiAgentEstUsd = aiCall ? Number(aiCall.cost_agent_usd) || 0 : 0;
    const aiLlmEstUsd   = aiCall ? Number(aiCall.cost_llm_usd)   || 0 : 0;
    const aiAgentUsd  = aiBilledUsd != null ? aiBilledUsd : aiAgentEstUsd;
    const aiCostChf   = aiAgentUsd * USD_TO_CHF_DISPLAY;
    const aiBilledSynced = aiBilledUsd != null;

    const internalCostChf = (twilioVoiceUsd + twilioRecUsd) * USD_TO_CHF_DISPLAY + trCostChf + aiCostChf;
    const margin = billedTotal - internalCostChf;
    const marginPct = internalCostChf > 0 && billedTotal > 0 ? (margin / billedTotal) * 100 : 100;

    const Row = ({ label, hint, internal, billed, billedHighlight }) => (
        <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr', gap: '0.5rem', padding: '0.4rem 0', borderBottom: '1px solid #e2e8f0', alignItems: 'baseline' }}>
            <div>
                <div style={{ fontSize: '0.78rem', fontWeight: 600 }}>{label}</div>
                {hint && <div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>{hint}</div>}
            </div>
            <div style={{ fontSize: '0.78rem', fontFeatureSettings: '"tnum"', color: '#64748b', textAlign: 'right' }}>{internal}</div>
            <div style={{ fontSize: '0.85rem', fontFeatureSettings: '"tnum"', textAlign: 'right', fontWeight: billedHighlight ? 700 : 600, color: billedHighlight ? '#0f172a' : '#0f172a' }}>{billed}</div>
        </div>
    );

    return (
        <div style={{ marginTop: '0.85rem', padding: '0.85rem 1rem', background: '#fff', border: '1px solid #e2e8f0', borderRadius: 8 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', marginBottom: '0.5rem' }}>
                <Icons.CreditCard className="w-4 h-4" />
                <strong style={{ fontSize: '0.78rem' }}>Détail du coût</strong>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr', gap: '0.5rem', paddingBottom: '0.3rem', borderBottom: '2px solid #e2e8f0', fontSize: '0.65rem', color: '#94a3b8', textTransform: 'uppercase', fontWeight: 700 }}>
                <div>Poste</div>
                <div style={{ textAlign: 'right' }}>Coût interne</div>
                <div style={{ textAlign: 'right' }}>Facturé client</div>
            </div>
            {(billedVoice > 0 || twilioVoiceUsd > 0) && (
                <Row
                    label={`Appel (${formatDuration(call.duration)})`}
                    hint={call.price_unit ? `Twilio voice` : null}
                    internal={twilioVoiceUsd > 0 ? `${twilioVoiceUsd.toFixed(4)} ${call.price_unit || 'USD'}` : '-'}
                    billed={billedVoice > 0 ? formatCurrency(billedVoice, 'CHF') : '-'}
                />
            )}
            {(billedRec > 0 || twilioRecUsd > 0) && (
                <Row
                    label="Enregistrement MP3"
                    hint="Twilio recording (storage + handling)"
                    internal={twilioRecUsd > 0 ? `${twilioRecUsd.toFixed(4)} ${call.recording_price_unit || 'USD'}` : '-'}
                    billed={billedRec > 0 ? formatCurrency(billedRec, 'CHF') : '-'}
                />
            )}
            {(billedTr > 0 || trCostChf > 0) && (
                <Row
                    label="Transcription IA"
                    hint={call.transcription_provider ? `Whisper · ${call.transcription_provider}` : 'Whisper'}
                    internal={trCostChf > 0 ? `${trCostChf.toFixed(4)} CHF` : '-'}
                    billed={billedTr > 0 ? formatCurrency(billedTr, 'CHF') : '-'}
                />
            )}
            {aiCall && aiAgentUsd > 0 && (
                <Row
                    label={`Agent vocal IA (${formatDuration(Math.round((aiCall.duration_ms || 0) / 1000))})${aiBilledSynced ? ' ✓ facturé' : ' ~estimé'}`}
                    hint={`Deepgram Voice Agent · ${aiCall.voice || ''} · ${aiCall.llm_tokens_in || 0} in / ${aiCall.llm_tokens_out || 0} out · ${aiCall.turns_user || 0} tours${aiBilledSynced ? '' : ' (estim. — coût réel dans qq min)'}`}
                    internal={`${aiAgentUsd.toFixed(4)} USD`}
                    billed="-"
                />
            )}
            <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr', gap: '0.5rem', padding: '0.55rem 0 0.2rem', alignItems: 'baseline' }}>
                <div style={{ fontSize: '0.85rem', fontWeight: 700 }}>Total</div>
                <div style={{ fontSize: '0.78rem', fontFeatureSettings: '"tnum"', color: '#64748b', textAlign: 'right' }}>
                    ~{internalCostChf.toFixed(4)} CHF
                </div>
                <div style={{ fontSize: '0.95rem', fontFeatureSettings: '"tnum"', textAlign: 'right', fontWeight: 800 }}>
                    {formatCurrency(billedTotal, call.billed_currency || 'CHF')}
                </div>
            </div>
            {internalCostChf > 0 && billedTotal > 0 && (
                <div style={{ marginTop: '0.4rem', padding: '0.4rem 0.6rem', background: margin > 0 ? '#f0fdf4' : '#fef2f2', borderRadius: 6, fontSize: '0.72rem', display: 'flex', justifyContent: 'space-between' }}>
                    <span style={{ color: margin > 0 ? '#15803d' : '#b91c1c', fontWeight: 600 }}>Marge brute</span>
                    <span style={{ color: margin > 0 ? '#15803d' : '#b91c1c', fontFeatureSettings: '"tnum"', fontWeight: 700 }}>
                        {margin >= 0 ? '+' : ''}{margin.toFixed(4)} CHF · {marginPct.toFixed(0)}%
                    </span>
                </div>
            )}
            {call.call_sid && (
                <div style={{ marginTop: '0.6rem', display: 'flex', justifyContent: 'flex-end' }}>
                    <button className="btn btn-secondary btn-sm" onClick={() => setShowBotAnalysis(true)}>
                        🔍 Analyse Bot — timeline complète {aiCall ? '' : '(si dispo)'}
                    </button>
                </div>
            )}
            {showBotAnalysis && (
                <BotAnalysisModal callSid={call.call_sid} onClose={() => setShowBotAnalysis(false)} />
            )}
        </div>
    );
}

function formatDuration(seconds) {
    if (!seconds) return '-';
    const m = Math.floor(seconds / 60);
    const s = seconds % 60;
    if (m === 0) return `${s}s`;
    return `${m}m${s > 0 ? String(s).padStart(2, '0') + 's' : ''}`;
}

// SQLite datetime('now') retourne du UTC sans suffixe Z. On force l'interprétation
// UTC pour que getHours() retourne l'heure locale du navigateur (Europe/Zurich, etc.).
function parseUtcDate(dateStr) {
    if (!dateStr) return null;
    if (dateStr instanceof Date) return dateStr;
    if (typeof dateStr === 'number') return new Date(dateStr);
    const s = String(dateStr).trim();
    if (/Z$|[+-]\d{2}:?\d{2}$/.test(s)) return new Date(s);
    if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(s)) {
        return new Date(s.replace(' ', 'T') + 'Z');
    }
    return new Date(s);
}

function formatDateTime(dateStr) {
    const d = parseUtcDate(dateStr);
    if (!d) return '-';
    const day = String(d.getDate()).padStart(2, '0');
    const month = String(d.getMonth() + 1).padStart(2, '0');
    const year = d.getFullYear();
    const h = String(d.getHours()).padStart(2, '0');
    const m = String(d.getMinutes()).padStart(2, '0');
    return `${day}/${month}/${year} ${h}:${m}`;
}

function formatTimeOnly(dateStr) {
    const d = parseUtcDate(dateStr);
    if (!d) return '';
    return d.toLocaleTimeString('fr-CH', { hour: '2-digit', minute: '2-digit' });
}

function formatDateShort(dateStr) {
    const d = parseUtcDate(dateStr);
    if (!d) return '';
    const day = String(d.getDate()).padStart(2, '0');
    const month = String(d.getMonth() + 1).padStart(2, '0');
    const year = String(d.getFullYear()).slice(-2);
    return `${day}/${month}/${year}`;
}

const CallDirectionIcon = ({ direction }) => {
    if (direction === 'outbound') {
        return (
            <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" style={{ color: '#2563eb' }}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M5 19l14-14m0 0h-10m10 0v10" />
            </svg>
        );
    }
    return (
        <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" style={{ color: '#16a34a' }}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M19 5L5 19m0 0h10M5 19V9" />
        </svg>
    );
};

const CallStatusDot = ({ status }) => {
    const colors = {
        completed: '#22c55e', ringing: '#3b82f6', 'in-progress': '#3b82f6',
        busy: '#f59e0b', 'no-answer': '#f59e0b', failed: '#ef4444',
        canceled: '#9ca3af', missed: '#ef4444',
    };
    return <span style={{ width: 8, height: 8, borderRadius: '50%', background: colors[status] || '#9ca3af', display: 'inline-block', flexShrink: 0 }} />;
};

// ==================== DEMANDES VIEW (Rappels + captures bot IA) ====================
const DemandesView = () => {
    const { notify } = useApp();
    const [requests, setRequests] = useState([]);
    const [total, setTotal] = useState(0);
    const [loading, setLoading] = useState(true);
    const [statusFilter, setStatusFilter] = useState('pending');
    const [sourceFilter, setSourceFilter] = useState('');
    const [expandedId, setExpandedId] = useState(null);

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const params = new URLSearchParams();
            if (statusFilter) params.set('status', statusFilter);
            if (sourceFilter) params.set('source', sourceFilter);
            params.set('limit', '200');
            const r = await api.get(`/api/callback-requests?${params}`);
            if (r?.error) { notify?.error?.(r.error); return; }
            setRequests(r.requests || []);
            setTotal(r.total || 0);
        } catch (e) { notify?.error?.('Erreur chargement demandes'); }
        finally { setLoading(false); }
    }, [statusFilter, sourceFilter, notify]);
    useEffect(() => { load(); }, [load]);

    const TYPE_META = {
        ai_appointment:            { label: 'RDV',           icon: '📅', cls: 'badge-info' },
        ai_reservation_restaurant: { label: 'Resto',         icon: '🍽', cls: 'badge-info' },
        ai_reservation_taxi:       { label: 'Taxi',          icon: '🚕', cls: 'badge-info' },
        ai_message:                { label: 'Message IA',    icon: '💬', cls: 'badge-info' },
        absence_menu:              { label: 'Rappel (menu)', icon: '☎️', cls: 'badge-gray' },
        manual:                    { label: 'Manuel',        icon: '✍️', cls: 'badge-gray' },
    };
    const typeBadge = (src) => {
        const meta = TYPE_META[src] || (src && src.startsWith('ai_')
            ? { label: src.replace(/^ai_/, ''), icon: '🤖', cls: 'badge-info' }
            : { label: src || 'Rappel', icon: '☎️', cls: 'badge-gray' });
        return <span className={`badge ${meta.cls}`} title={src || ''}>{meta.icon} {meta.label}</span>;
    };
    const statusBadge = (s) => {
        if (s === 'pending') return <span className="badge badge-warning">À traiter</span>;
        if (s === 'done') return <span className="badge badge-success">Traité</span>;
        if (s === 'cancelled') return <span className="badge badge-gray">Annulé</span>;
        return <span className="badge badge-gray">{s}</span>;
    };
    const parsePayload = (raw) => {
        if (!raw) return null;
        if (typeof raw === 'object') return raw;
        try { return JSON.parse(raw); } catch { return null; }
    };

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Demandes</h1>
                    <p className="page-subtitle">Toutes les demandes des appelants : rappels humains + RDV / réservations / messages captés par le bot IA. {total} entrée{total > 1 ? 's' : ''}.</p>
                </div>
            </div>
            <div className="page-content">
                <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '0.75rem', flexWrap: 'wrap' }}>
                    {[
                        { id: 'pending', label: 'À traiter' },
                        { id: 'done', label: 'Traités' },
                        { id: 'cancelled', label: 'Annulés' },
                        { id: '', label: 'Tous' },
                    ].map(f => (
                        <button key={f.id || 'all'}
                            className={`btn btn-sm ${statusFilter === f.id ? 'btn-primary' : 'btn-secondary'}`}
                            onClick={() => setStatusFilter(f.id)}>{f.label}</button>
                    ))}
                </div>
                <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem', flexWrap: 'wrap', alignItems: 'center' }}>
                    <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Type :</span>
                    {[
                        { id: '',                          label: 'Tous' },
                        { id: 'ai',                        label: '🤖 IA' },
                        { id: 'ai_appointment',            label: '📅 RDV' },
                        { id: 'ai_reservation_restaurant', label: '🍽 Resto' },
                        { id: 'ai_reservation_taxi',       label: '🚕 Taxi' },
                        { id: 'ai_message',                label: '💬 Message IA' },
                        { id: 'human',                     label: '☎️ Humain' },
                    ].map(f => (
                        <button key={f.id || 'src-all'}
                            className={`btn btn-sm ${sourceFilter === f.id ? 'btn-primary' : 'btn-secondary'}`}
                            onClick={() => setSourceFilter(f.id)}>{f.label}</button>
                    ))}
                </div>

                {loading && <div style={{ padding: '2rem', textAlign: 'center', color: 'var(--text-muted)' }}>Chargement…</div>}
                {!loading && requests.length === 0 && (
                    <div className="card" style={{ padding: '3rem 2rem', textAlign: 'center', color: 'var(--text-muted)' }}>
                        <Icons.Refresh className="w-12 h-12" style={{ margin: '0 auto 1rem', opacity: 0.4 }} />
                        <h3 style={{ margin: '0 0 0.5rem', fontSize: '1rem' }}>Aucune demande</h3>
                    </div>
                )}
                {!loading && requests.length > 0 && (
                    <div className="card">
                        <div className="table-container">
                            <table className="data-table">
                                <thead>
                                    <tr>
                                        <th>Statut</th>
                                        <th>Type</th>
                                        <th>Reçu le</th>
                                        <th>Client</th>
                                        <th>Ligne</th>
                                        <th>Appelant</th>
                                        <th>Détails / Notes</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {requests.map(r => {
                                        const payload = parsePayload(r.capture_payload);
                                        const isAi = (r.source || '').startsWith('ai_');
                                        const isOpen = expandedId === r.id;
                                        return (
                                            <React.Fragment key={r.id}>
                                                <tr>
                                                    <td>{statusBadge(r.status)}</td>
                                                    <td>{typeBadge(r.source)}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{formatDateTime(r.created_at)}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>{r.client_name || `#${r.client_id}`}</td>
                                                    <td style={{ fontSize: '0.8rem' }}>
                                                        <div style={{ fontWeight: 600 }}>{r.line_label || '-'}</div>
                                                        <div style={{ fontFamily: 'ui-monospace, monospace', color: 'var(--text-muted)', fontSize: '0.7rem' }}>{r.line_phone}</div>
                                                    </td>
                                                    <td style={{ fontFamily: 'ui-monospace, monospace', fontSize: '0.8rem', fontWeight: 600 }}>{r.caller_number || '-'}</td>
                                                    <td style={{ fontSize: '0.8rem', maxWidth: 360 }}>
                                                        {r.notes && <div style={{ marginBottom: '0.25rem' }}>{r.notes}</div>}
                                                        {isAi && payload && (
                                                            <button className="btn btn-secondary btn-sm" style={{ padding: '0.15rem 0.5rem', fontSize: '0.7rem' }}
                                                                onClick={() => setExpandedId(isOpen ? null : r.id)}>
                                                                {isOpen ? '▲ Masquer' : '▼ Détails'}
                                                            </button>
                                                        )}
                                                    </td>
                                                </tr>
                                                {isOpen && payload && (
                                                    <tr>
                                                        <td colSpan={7} style={{ background: 'var(--bg-muted, #fafafa)', padding: '0.75rem 1rem' }}>
                                                            <div style={{ fontSize: '0.8rem', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '0.5rem 1rem' }}>
                                                                {Object.entries(payload).map(([k, v]) => (
                                                                    <div key={k}>
                                                                        <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>{k.replace(/_/g, ' ')}</div>
                                                                        <div style={{ fontWeight: 500, wordBreak: 'break-word' }}>{typeof v === 'object' ? JSON.stringify(v) : String(v || '—')}</div>
                                                                    </div>
                                                                ))}
                                                            </div>
                                                            {r.call_sid && (
                                                                <div style={{ marginTop: '0.5rem', fontSize: '0.7rem', color: 'var(--text-muted)', fontFamily: 'ui-monospace, monospace' }}>
                                                                    call_sid : {r.call_sid}
                                                                </div>
                                                            )}
                                                        </td>
                                                    </tr>
                                                )}
                                            </React.Fragment>
                                        );
                                    })}
                                </tbody>
                            </table>
                        </div>
                    </div>
                )}
            </div>
        </>
    );
};

const CallsView = () => {
    const { notify } = useApp();
    const [calls, setCalls] = useState({ calls: [], total: 0, page: 1, total_pages: 1 });
    const [lines, setLines] = useState([]);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);
    const [page, setPage] = useState(1);
    const [filterLine, setFilterLine] = useState('');
    const [filterStatus, setFilterStatus] = useState('');
    const [filterDirection, setFilterDirection] = useState('');
    const [search, setSearch] = useState('');
    const debouncedSearch = useDebouncedValue(search, 350);
    const [expandedCall, setExpandedCall] = useState(null);
    const reqIdRef = useRef(0);
    const initialLoadRef = useRef(true);

    const buildUrl = useCallback((p) => {
        const params = new URLSearchParams();
        params.set('page', String(p));
        params.set('limit', '30');
        if (filterLine) params.set('line_id', filterLine);
        if (filterStatus) params.set('status', filterStatus);
        if (filterDirection) params.set('direction', filterDirection);
        if (debouncedSearch.trim()) params.set('q', debouncedSearch.trim());
        return `/api/calls?${params.toString()}`;
    }, [filterLine, filterStatus, filterDirection, debouncedSearch]);

    const loadCalls = useCallback(async (p, opts = {}) => {
        const reqId = ++reqIdRef.current;
        if (opts.background) setRefreshing(true); else setLoading(true);
        try {
            const data = await api.get(buildUrl(p));
            if (reqId !== reqIdRef.current) return;
            setCalls(data);
        } catch (e) {
            if (reqId === reqIdRef.current) notify.error('Erreur chargement appels');
        } finally {
            if (reqId === reqIdRef.current) {
                setLoading(false);
                setRefreshing(false);
            }
        }
    }, [buildUrl, notify]);

    useEffect(() => {
        (async () => {
            try {
                const data = await api.get('/api/lines');
                setLines(data.lines || []);
            } catch (e) {}
        })();
    }, []);

    useEffect(() => {
        if (page !== 1) { setPage(1); return; }
        loadCalls(1);
    }, [filterLine, filterStatus, filterDirection, debouncedSearch]);

    useEffect(() => {
        loadCalls(page, { background: !initialLoadRef.current });
        initialLoadRef.current = false;
    }, [page]);

    const handlePage = (p) => {
        if (p < 1 || p > calls.total_pages) return;
        setPage(p);
    };

    const visibleCalls = calls.calls || [];

    const stats = useMemo(() => {
        const all = visibleCalls;
        const completed = all.filter(c => c.status === 'completed').length;
        const missed = all.filter(c => ['missed', 'no-answer', 'busy'].includes(c.status)).length;
        const totalDuration = all.reduce((sum, c) => sum + (c.duration || 0), 0);
        const totalBilled = all.reduce((sum, c) => sum + (c.billed_price || 0), 0);
        const unsynced = all.filter(c => !c.is_price_synced).length;
        return { completed, missed, totalDuration, totalBilled, unsynced };
    }, [visibleCalls]);

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Appels</h1>
                    <p className="page-subtitle">{calls.total} appels au total</p>
                </div>
                <div className="page-actions" style={{ display: 'flex', gap: '0.5rem' }}>
                    <button className="btn btn-secondary btn-sm" id="syncBtn" onClick={async (e) => {
                        const btn = e.currentTarget;
                        btn.disabled = true;
                        btn.textContent = 'Synchronisation...';
                        try {
                            const res = await api.post('/api/calls/sync-prices');
                            notify.success(res.synced > 0
                                ? `${res.synced}/${res.total} appels synchronises${res.errors > 0 ? ` (${res.errors} erreurs)` : ''}`
                                : `${res.total} appels verifies, tous a jour`);
                            loadCalls(page, { background: true });
                        } catch(e) { notify.error('Erreur sync'); }
                        finally { btn.disabled = false; btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg> Sync prix'; }
                    }}>
                        <Icons.CreditCard className="w-4 h-4" /> Sync prix
                    </button>
                    <button className="btn btn-secondary btn-sm" onClick={() => loadCalls(page, { background: true })} disabled={refreshing}>
                        <Icons.Refresh className="w-4 h-4" /> {refreshing ? 'Mise à jour...' : 'Actualiser'}
                    </button>
                </div>
            </div>

            <div className="page-content">
                <div className="stats-grid">
                    <div className="stat-card">
                        <div className="stat-icon green"><Icons.Phone className="w-6 h-6" /></div>
                        <div><div className="stat-label">Repondus</div><div className="stat-value">{stats.completed}</div></div>
                    </div>
                    <div className="stat-card">
                        <div className="stat-icon red"><Icons.PhoneIncoming className="w-6 h-6" /></div>
                        <div><div className="stat-label">Manques</div><div className="stat-value">{stats.missed}</div></div>
                    </div>
                    <div className="stat-card">
                        <div className="stat-icon blue">
                            <svg xmlns="http://www.w3.org/2000/svg" className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
                        </div>
                        <div><div className="stat-label">Duree totale</div><div className="stat-value">{formatDuration(stats.totalDuration)}</div></div>
                    </div>
                    <div className="stat-card">
                        <div className="stat-icon purple"><Icons.CreditCard className="w-6 h-6" /></div>
                        <div>
                            <div className="stat-label">Cout facture</div>
                            <div className="stat-value">{formatCurrency(stats.totalBilled)}</div>
                            {stats.unsynced > 0 && <div style={{ fontSize: '0.7rem', color: 'var(--warning)', marginTop: '0.15rem' }}>{stats.unsynced} non syncs</div>}
                        </div>
                    </div>
                </div>

                <div className="card" style={{ overflow: 'hidden' }}>
                    <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid var(--border)', display: 'flex', gap: '0.75rem', flexWrap: 'wrap', alignItems: 'center' }}>
                        <div style={{ position: 'relative', flex: '1 1 200px', minWidth: '160px' }}>
                            <Icons.Search className="w-4 h-4" style={{ position: 'absolute', left: '0.75rem', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted)' }} />
                            <input className="form-input" placeholder="Rechercher un numero..."
                                value={search} onChange={(e) => setSearch(e.target.value)}
                                style={{ paddingLeft: '2.25rem', marginBottom: 0 }} />
                        </div>
                        <select className="form-input" style={{ flex: '0 0 auto', width: 'auto', minWidth: '160px', marginBottom: 0 }}
                            value={filterLine} onChange={(e) => setFilterLine(e.target.value)}>
                            <option value="">Toutes les lignes</option>
                            {lines.map(l => <option key={l.id} value={l.id}>{l.phone_number} {l.label ? `(${l.label})` : ''}</option>)}
                        </select>
                        <select className="form-input" style={{ flex: '0 0 auto', width: 'auto', minWidth: '140px', marginBottom: 0 }}
                            value={filterStatus} onChange={(e) => setFilterStatus(e.target.value)}>
                            <option value="">Tous les statuts</option>
                            <option value="missed">Manqués (non-réponse, occupé)</option>
                            <option value="transfer_failed">Transferts manqués (flat 0.02 CHF)</option>
                            {Object.entries(CALL_STATUS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
                        </select>
                        <select className="form-input" style={{ flex: '0 0 auto', width: 'auto', minWidth: '130px', marginBottom: 0 }}
                            value={filterDirection} onChange={(e) => setFilterDirection(e.target.value)}>
                            <option value="">Entrants + Sortants</option>
                            <option value="inbound">Entrants</option>
                            <option value="outbound">Sortants</option>
                        </select>
                    </div>

                    {loading ? (
                        <div style={{ padding: '1rem' }}><SkeletonCards count={6} columns={1} /></div>
                    ) : visibleCalls.length === 0 ? (
                        <div style={{ padding: '4rem 2rem', textAlign: 'center' }}>
                            <div style={{ width: 64, height: 64, borderRadius: '50%', background: 'var(--bg-secondary)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 1rem' }}>
                                <Icons.Phone className="w-7 h-7" style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
                            </div>
                            <p style={{ fontWeight: 600, fontSize: '0.95rem', margin: '0 0 0.25rem', color: 'var(--text-secondary)' }}>Aucun appel trouvé</p>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: 0 }}>Les appels apparaîtront ici une fois reçus</p>
                        </div>
                    ) : (
                        <div style={{ opacity: refreshing ? 0.6 : 1, transition: 'opacity 0.15s ease' }}>
                            {(() => {
                                const groups = {};
                                const order = [];
                                for (const c of visibleCalls) {
                                    const d = new Date(c.created_at);
                                    const today = new Date(); today.setHours(0,0,0,0);
                                    const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1);
                                    const dDay = new Date(d); dDay.setHours(0,0,0,0);
                                    let key, label;
                                    if (dDay.getTime() === today.getTime()) { key = 'today'; label = "Aujourd'hui"; }
                                    else if (dDay.getTime() === yesterday.getTime()) { key = 'yesterday'; label = 'Hier'; }
                                    else { key = dDay.toISOString().slice(0, 10); label = d.toLocaleDateString('fr-CH', { weekday: 'long', day: 'numeric', month: 'long', year: today.getFullYear() === d.getFullYear() ? undefined : 'numeric' }); }
                                    if (!groups[key]) { groups[key] = { label, items: [] }; order.push(key); }
                                    groups[key].items.push(c);
                                }
                                return order.map(key => {
                                    const g = groups[key];
                                    const dayTotal = g.items.reduce((s, c) => s + (c.billed_price || 0), 0);
                                    const dayMins = Math.round(g.items.reduce((s, c) => s + (c.duration || 0), 0) / 60);
                                    return (
                                        <div key={key}>
                                            <div style={{
                                                position: 'sticky', top: 0, zIndex: 5, background: '#fafbfc',
                                                borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)',
                                                padding: '0.5rem 1.25rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                                            }}>
                                                <span style={{ fontSize: '0.78rem', fontWeight: 700, textTransform: 'capitalize', color: '#475569', letterSpacing: '0.02em' }}>{g.label}</span>
                                                <span style={{ fontSize: '0.72rem', color: 'var(--text-muted)', fontFeatureSettings: '"tnum"' }}>
                                                    {g.items.length} appel{g.items.length > 1 ? 's' : ''} · {dayMins}min · {formatCurrency(dayTotal, 'CHF')}
                                                </span>
                                            </div>
                                            {g.items.map(call => {
                                                const isTransferFailed = call.call_outcome === 'transfer_failed';
                                                const st = isTransferFailed
                                                    ? { label: 'Transfert manqué', color: 'badge-warning' }
                                                    : (CALL_STATUS[call.status] || { label: call.status, color: 'badge-gray' });
                                                const isExpanded = expandedCall === call.id;
                                                const lineType = LINE_TYPES[call.line_type] || null;
                                                const isOut = call.direction === 'outbound';
                                                const peer = isOut ? call.to_number : call.from_number;
                                                const isFailed = ['failed', 'busy', 'no-answer', 'missed', 'canceled'].includes(call.status);
                                                const dirColor = isFailed ? '#ef4444' : (isOut ? '#2563eb' : '#16a34a');
                                                return (
                                                    <div key={call.id} style={{ borderBottom: '1px solid #f1f5f9' }}>
                                                        <div onClick={() => setExpandedCall(isExpanded ? null : call.id)}
                                                            style={{
                                                                cursor: 'pointer', display: 'grid',
                                                                gridTemplateColumns: '44px 1fr 200px 160px 100px 110px',
                                                                alignItems: 'center', gap: '1rem', padding: '0.75rem 1.25rem',
                                                                background: isExpanded ? '#f8fafc' : 'transparent',
                                                                transition: 'background 0.12s',
                                                            }}
                                                            onMouseEnter={(e) => { if (!isExpanded) e.currentTarget.style.background = '#fafbfc'; }}
                                                            onMouseLeave={(e) => { if (!isExpanded) e.currentTarget.style.background = 'transparent'; }}>
                                                            <div style={{
                                                                width: 40, height: 40, borderRadius: '50%',
                                                                background: `${dirColor}15`, color: dirColor,
                                                                display: 'flex', alignItems: 'center', justifyContent: 'center',
                                                                flexShrink: 0,
                                                            }}>
                                                                <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
                                                                    {isOut ? (
                                                                        <path strokeLinecap="round" strokeLinejoin="round" d="M5 19l14-14m0 0h-10m10 0v10" />
                                                                    ) : (
                                                                        <path strokeLinecap="round" strokeLinejoin="round" d="M19 5L5 19m0 0h10M5 19V9" />
                                                                    )}
                                                                </svg>
                                                            </div>
                                                            <div style={{ minWidth: 0 }}>
                                                                <div style={{ fontWeight: 600, fontSize: '0.92rem', fontFeatureSettings: '"tnum"', letterSpacing: '0.01em', color: '#0f172a' }}>
                                                                    {peer || '-'}
                                                                </div>
                                                                <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)', display: 'flex', alignItems: 'center', gap: '0.4rem', marginTop: 2 }}>
                                                                    <CallStatusDot status={call.status} />
                                                                    <span>{st.label}</span>
                                                                    {call.recording_url && (
                                                                        <>
                                                                            <span>•</span>
                                                                            <span style={{ color: '#6366f1' }}>🔊 enregistré</span>
                                                                        </>
                                                                    )}
                                                                </div>
                                                            </div>
                                                            <div style={{ minWidth: 0, fontSize: '0.78rem' }}>
                                                                {call.line_phone ? (
                                                                    <>
                                                                        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                                                                            {lineType && <span className={`badge ${lineType.color}`} style={{ fontSize: '0.62rem', padding: '0.1rem 0.35rem' }}>{lineType.label}</span>}
                                                                            <span style={{ fontWeight: 500, color: '#0f172a' }}>{call.line_label || call.line_phone}</span>
                                                                        </div>
                                                                        {call.line_label && <div style={{ color: 'var(--text-muted)', fontSize: '0.72rem', fontFeatureSettings: '"tnum"' }}>{call.line_phone}</div>}
                                                                    </>
                                                                ) : <span style={{ color: 'var(--text-muted)' }}>-</span>}
                                                            </div>
                                                            <div style={{ fontSize: '0.78rem', color: 'var(--text-muted)' }}>
                                                                <div style={{ fontWeight: 500, color: '#0f172a', fontFeatureSettings: '"tnum"' }}>{formatTimeOnly(call.created_at)}</div>
                                                                <div style={{ fontFeatureSettings: '"tnum"' }}>{formatDateShort(call.created_at)}</div>
                                                            </div>
                                                            <div style={{ textAlign: 'right', fontFeatureSettings: '"tnum"' }}>
                                                                <div style={{ fontSize: '0.85rem', fontWeight: 600, color: call.duration > 0 ? '#0f172a' : 'var(--text-muted)' }}>
                                                                    {formatDuration(call.duration)}
                                                                </div>
                                                            </div>
                                                            <div style={{ textAlign: 'right', fontFeatureSettings: '"tnum"' }}>
                                                                {call.is_price_synced ? (
                                                                    <>
                                                                        <div style={{ fontSize: '0.92rem', fontWeight: 700, color: call.billed_price > 0 ? '#0f172a' : '#94a3b8' }}>
                                                                            {call.billed_price > 0 ? formatCurrency(call.billed_price, call.billed_currency || 'CHF') : '0.00'}
                                                                        </div>
                                                                        {call.price > 0 && <div style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>{formatCurrency(call.price, call.price_unit || 'USD')}</div>}
                                                                    </>
                                                                ) : (
                                                                    <span style={{ display: 'inline-block', fontSize: '0.65rem', color: '#d97706', background: '#fef3c7', padding: '0.15rem 0.5rem', borderRadius: 999, fontWeight: 600 }}>non sync</span>
                                                                )}
                                                            </div>
                                                        </div>
                                                        {isExpanded && (
                                                            <div style={{ background: '#f8fafc', padding: '1rem 1.25rem 1.25rem 4.5rem', borderTop: '1px solid #e2e8f0' }}>
                                                                {call.recording_url && (
                                                                    <div style={{ marginBottom: '0.75rem' }}>
                                                                        <audio controls src={call.recording_url} style={{ width: '100%', maxWidth: 420, height: 36 }} />
                                                                    </div>
                                                                )}
                                                                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: '0.85rem', fontSize: '0.78rem' }}>
                                                                    <div>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>De</div>
                                                                        <div style={{ fontWeight: 500, fontFeatureSettings: '"tnum"' }}>{call.from_number || '-'}</div>
                                                                    </div>
                                                                    <div>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>Vers</div>
                                                                        <div style={{ fontWeight: 500, fontFeatureSettings: '"tnum"' }}>{call.to_number || '-'}</div>
                                                                    </div>
                                                                    <div>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>Direction</div>
                                                                        <div>{isOut ? '↗ Sortant' : '↘ Entrant'}</div>
                                                                    </div>
                                                                    <div>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>Date complète</div>
                                                                        <div>{formatDateTime(call.created_at)}</div>
                                                                    </div>
                                                                    <div>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>Sync</div>
                                                                        <div>{call.is_price_synced ? `✓ ${formatDateTime(call.price_synced_at)}` : '⏳ en attente'}</div>
                                                                    </div>
                                                                    <div style={{ gridColumn: 'span 2' }}>
                                                                        <div style={{ color: '#94a3b8', fontSize: '0.68rem', textTransform: 'uppercase', fontWeight: 700, letterSpacing: '0.06em', marginBottom: 2 }}>Call SID</div>
                                                                        <div style={{ fontFamily: 'monospace', fontSize: '0.7rem', wordBreak: 'break-all', color: '#475569' }}>{call.call_sid || '-'}</div>
                                                                    </div>
                                                                </div>
                                                                <CallCostBreakdown call={call} />
                                                            </div>
                                                        )}
                                                    </div>
                                                );
                                            })}
                                        </div>
                                    );
                                });
                            })()}
                        </div>
                    )}

                    {calls.total_pages > 1 && (
                        <div style={{ padding: '0.75rem 1.25rem', borderTop: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: 'var(--bg-secondary)' }}>
                            <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>
                                {visibleCalls.length} sur {calls.total} appels
                            </span>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                <button className="btn btn-secondary btn-sm" onClick={() => handlePage(page - 1)} disabled={page <= 1} style={{ padding: '0.3rem 0.6rem' }}>
                                    <Icons.ChevronLeft className="w-4 h-4" />
                                </button>
                                <span style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)', minWidth: '80px', textAlign: 'center' }}>
                                    {page} / {calls.total_pages}
                                </span>
                                <button className="btn btn-secondary btn-sm" onClick={() => handlePage(page + 1)} disabled={page >= calls.total_pages} style={{ padding: '0.3rem 0.6rem' }}>
                                    <Icons.ChevronRight className="w-4 h-4" />
                                </button>
                            </div>
                        </div>
                    )}
                </div>
            </div>
        </>
    );
};

// ==================== SMS ====================
const SmsView = () => {
    const { notify } = useApp();
    const [data, setData] = useState({ sms: [], total: 0, page: 1, total_pages: 1 });
    const [loading, setLoading] = useState(true);
    const [page, setPage] = useState(1);

    const load = useCallback(async (p = page) => {
        setLoading(true);
        try {
            const res = await api.get(`/api/sms?page=${p}&limit=30`);
            setData({
                sms: Array.isArray(res?.sms) ? res.sms : [],
                total: res?.total || 0,
                page: res?.page || p,
                total_pages: res?.total_pages || 1,
            });
        } catch (e) { notify.error('Erreur chargement SMS'); }
        finally { setLoading(false); }
    }, [page, notify]);

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

    const SMS_STATUS = { sent: { label: 'Envoye', color: 'badge-success' }, failed: { label: 'Echoue', color: 'badge-danger' }, error: { label: 'Erreur', color: 'badge-danger' } };

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">SMS</h1>
                    <p className="page-subtitle">{data.total} SMS envoyes</p>
                </div>
                <div className="page-actions">
                    <button className="btn btn-secondary btn-sm" onClick={() => { setPage(1); load(1); }}>
                        <Icons.Refresh className="w-4 h-4" /> Actualiser
                    </button>
                </div>
            </div>
            <div className="page-content">
                <div className="card" style={{ overflow: 'hidden' }}>
                    {loading ? (
                        <SkeletonTable rows={10} cols={6} />
                    ) : data.sms.length === 0 ? (
                        <div style={{ padding: '4rem 2rem', textAlign: 'center' }}>
                            <div style={{ width: 64, height: 64, borderRadius: '50%', background: 'var(--bg-secondary)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 1rem' }}>
                                <Icons.Send className="w-7 h-7" style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
                            </div>
                            <p style={{ fontWeight: 600, fontSize: '0.95rem', margin: '0 0 0.25rem', color: 'var(--text-secondary)' }}>Aucun SMS envoye</p>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: 0 }}>Les SMS apparaitront ici</p>
                        </div>
                    ) : (
                        <div className="table-container">
                            <table className="table">
                                <thead>
                                    <tr>
                                        <th>Date</th>
                                        <th>Destinataire</th>
                                        <th>Expediteur</th>
                                        <th>Message</th>
                                        <th>Ligne</th>
                                        <th>Statut</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {data.sms.map(s => {
                                        const st = SMS_STATUS[s.status] || { label: s.status, color: 'badge-gray' };
                                        return (
                                            <tr key={s.id}>
                                                <td style={{ whiteSpace: 'nowrap', fontSize: '0.8rem' }}>{formatDateTime(s.created_at)}</td>
                                                <td style={{ fontWeight: 500, fontFeatureSettings: '"tnum"' }}>{s.to_number || '-'}</td>
                                                <td style={{ fontSize: '0.85rem' }}>{s.sender || '-'}</td>
                                                <td style={{ fontSize: '0.8rem', maxWidth: '250px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.message || '-'}</td>
                                                <td style={{ fontSize: '0.8rem' }}>{s.line_label || s.line_phone || '-'}</td>
                                                <td><span className={`badge ${st.color}`}>{st.label}</span></td>
                                            </tr>
                                        );
                                    })}
                                </tbody>
                            </table>
                        </div>
                    )}
                    {data.total_pages > 1 && (
                        <div style={{ padding: '0.75rem 1.25rem', borderTop: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: 'var(--bg-secondary)' }}>
                            <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>{data.sms.length} sur {data.total}</span>
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                                <button className="btn btn-secondary btn-sm" onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} style={{ padding: '0.3rem 0.6rem' }}><Icons.ChevronLeft className="w-4 h-4" /></button>
                                <span style={{ fontSize: '0.8rem', fontWeight: 600, minWidth: '80px', textAlign: 'center' }}>{page} / {data.total_pages}</span>
                                <button className="btn btn-secondary btn-sm" onClick={() => setPage(p => Math.min(data.total_pages, p + 1))} disabled={page >= data.total_pages} style={{ padding: '0.3rem 0.6rem' }}><Icons.ChevronRight className="w-4 h-4" /></button>
                            </div>
                        </div>
                    )}
                </div>
            </div>
        </>
    );
};

// ==================== WHATSAPP ====================
// Format un prix Twilio (USD) avec sa devise pour affichage compact.
const fmtMsgPrice = (price, unit, direction, isSynced) => {
    if (direction === 'inbound') return <span style={{ color: 'var(--text-muted)' }}>gratuit</span>;
    if (price === null || price === undefined) {
        if (isSynced === -1) return <span style={{ color: '#94a3b8', fontSize: '0.7rem' }} title="Prix non disponible">n/a</span>;
        return <span style={{ color: '#f59e0b', fontSize: '0.7rem' }} title="Prix en attente Twilio">~</span>;
    }
    const v = parseFloat(price);
    const u = unit || 'USD';
    return <span style={{ fontWeight: 600, fontFeatureSettings: '"tnum"' }}>{v.toFixed(4)} {u}</span>;
};

const WhatsappView = () => {
    const { notify } = useApp();
    const [data, setData] = useState({ whatsapp: [], total: 0, page: 1, total_pages: 1, stats: null });
    const [loading, setLoading] = useState(true);
    const [page, setPage] = useState(1);

    const load = useCallback(async (p = page) => {
        setLoading(true);
        try {
            const res = await api.get(`/api/whatsapp?page=${p}&limit=30`);
            setData({
                whatsapp: Array.isArray(res?.whatsapp) ? res.whatsapp : [],
                total: res?.total || 0,
                page: res?.page || p,
                total_pages: res?.total_pages || 1,
                stats: res?.stats || null,
            });
        } catch (e) { notify.error('Erreur chargement WhatsApp'); }
        finally { setLoading(false); }
    }, [page, notify]);

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

    const stats = data.stats || {};
    const totalTwilio = parseFloat(stats.total_twilio_usd || 0);
    const totalAi     = parseFloat(stats.total_ai_usd || 0);
    const totalBilled = parseFloat(stats.total_billed_chf || 0);

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">WhatsApp</h1>
                    <p className="page-subtitle">
                        {data.total} messages · {stats.out_count || 0} sortants · {stats.in_count || 0} entrants
                        {stats.pending_sync > 0 && <span style={{ color: '#f59e0b' }}> · {stats.pending_sync} prix en attente</span>}
                    </p>
                </div>
                <div className="page-actions" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap' }}>
                    <div style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', display: 'flex', gap: '0.75rem', alignItems: 'center' }}>
                        <span title="Coût Twilio (USD)">Twilio: <strong style={{ color: 'var(--text-primary)' }}>{totalTwilio.toFixed(4)} $</strong></span>
                        <span title="Coût OpenAI (USD)">IA: <strong style={{ color: 'var(--text-primary)' }}>{totalAi.toFixed(4)} $</strong></span>
                        <span title="Total facturé clients">Facturé: <strong style={{ color: '#10b981' }}>{totalBilled.toFixed(2)} CHF</strong></span>
                    </div>
                    <button className="btn btn-secondary btn-sm" onClick={() => { setPage(1); load(1); }}>
                        <Icons.Refresh className="w-4 h-4" /> Actualiser
                    </button>
                </div>
            </div>
            <div className="page-content">
                <div className="card" style={{ overflow: 'hidden' }}>
                    {loading ? (
                        <SkeletonTable rows={10} cols={7} />
                    ) : data.whatsapp.length === 0 ? (
                        <div style={{ padding: '4rem 2rem', textAlign: 'center' }}>
                            <div style={{ width: 64, height: 64, borderRadius: '50%', background: 'var(--bg-secondary)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 1rem' }}>
                                <Icons.MessageSquare className="w-7 h-7" style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
                            </div>
                            <p style={{ fontWeight: 600, fontSize: '0.95rem', margin: '0 0 0.25rem', color: 'var(--text-secondary)' }}>Aucun message WhatsApp</p>
                            <p style={{ fontSize: '0.8rem', color: 'var(--text-muted)', margin: 0 }}>Les messages apparaitront ici</p>
                        </div>
                    ) : (
                        <div className="table-container">
                            <table className="table">
                                <thead>
                                    <tr>
                                        <th>Date</th>
                                        <th>Sens</th>
                                        <th>Contact</th>
                                        <th>Message</th>
                                        <th>Ligne</th>
                                        <th>Statut</th>
                                        <th style={{ textAlign: 'right' }} title="Coût Twilio (USD) - réel si dispo, ~ sinon estimé pour facturation">Twilio</th>
                                        <th style={{ textAlign: 'right' }} title="Coût IA (USD) si message généré par bot">IA</th>
                                        <th style={{ textAlign: 'right' }} title="Prix facturé client (CHF, avec marge)">Facturé</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {data.whatsapp.map(w => {
                                        const isOut = w.direction === 'outbound';
                                        const ai = parseFloat(w.ai_cost_usd || 0);
                                        const billed = parseFloat(w.billed_price || 0);
                                        return (
                                            <tr key={w.id}>
                                                <td style={{ whiteSpace: 'nowrap', fontSize: '0.8rem' }}>{formatDateTime(w.created_at)}</td>
                                                <td>
                                                    <span className={`badge ${isOut ? 'badge-info' : 'badge-gray'}`} style={{ fontSize: '0.65rem' }}>
                                                        {isOut ? `↗ ${w.sent_by || 'out'}` : '↘ in'}
                                                    </span>
                                                    {w.wa_category && isOut && <div style={{ fontSize: '0.6rem', color: 'var(--text-muted)', marginTop: 2 }}>{w.wa_category}</div>}
                                                </td>
                                                <td style={{ fontWeight: 500, fontFeatureSettings: '"tnum"' }}>{w.to_number || '-'}</td>
                                                <td style={{ fontSize: '0.8rem', maxWidth: '230px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={w.message || ''}>{w.message || '-'}</td>
                                                <td style={{ fontSize: '0.8rem' }}>{w.line_label || w.line_phone || '-'}</td>
                                                <td>
                                                    <span className={`badge ${(w.status === 'delivered' || w.status === 'read' || w.status === 'sent') ? 'badge-success' : (w.status === 'failed' || w.status === 'undelivered') ? 'badge-danger' : 'badge-gray'}`}>
                                                        {w.status || '-'}
                                                    </span>
                                                </td>
                                                <td style={{ textAlign: 'right', fontSize: '0.75rem', whiteSpace: 'nowrap', fontFeatureSettings: '"tnum"' }}>
                                                    {fmtMsgPrice(w.price, w.price_unit, w.direction, w.is_price_synced)}
                                                </td>
                                                <td style={{ textAlign: 'right', fontSize: '0.75rem', whiteSpace: 'nowrap', fontFeatureSettings: '"tnum"' }}>
                                                    {!isOut ? <span style={{ color: 'var(--text-muted)' }}>-</span> :
                                                     ai > 0 ? <span title={`${w.ai_input_tokens||0} in / ${w.ai_output_tokens||0} out`}>{ai.toFixed(5)} $<div style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>{(w.ai_input_tokens||0)+'+'+(w.ai_output_tokens||0)}t</div></span> :
                                                     <span style={{ color: 'var(--text-muted)' }}>-</span>}
                                                </td>
                                                <td style={{ textAlign: 'right', fontSize: '0.78rem', whiteSpace: 'nowrap', fontFeatureSettings: '"tnum"' }}>
                                                    {!isOut ? <span style={{ color: 'var(--text-muted)' }}>-</span> :
                                                     billed > 0 ? <span style={{ fontWeight: 700, color: w.wallet_charged ? '#10b981' : '#f59e0b' }} title={w.wallet_charged ? 'Wallet débité' : 'En attente débit'}>{billed.toFixed(3)} {w.billed_currency || 'CHF'}</span> :
                                                     <span style={{ color: 'var(--text-muted)' }}>-</span>}
                                                </td>
                                            </tr>
                                        );
                                    })}
                                </tbody>
                            </table>
                        </div>
                    )}
                </div>
            </div>
        </>
    );
};

// ==================== MODULES ADMIN VIEW ====================
const ModulesAdminView = () => {
    const { notify } = useApp();
    const [modules, setModules] = useState([]);
    const [loading, setLoading] = useState(true);
    const [editing, setEditing] = useState(null);
    const [showModal, setShowModal] = useState(false);
    const [showSubs, setShowSubs] = useState(null);
    const [subs, setSubs] = useState([]);
    const [form, setForm] = useState({
        code: '', name: '', description: '', category: 'general', icon: '',
        price_chf: 0, setup_fee_chf: 0, billing_period: 'month',
        is_active: 1, is_default: 0, trial_days: 0, sort_order: 0,
    });

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const r = await api.get('/api/admin/modules');
            setModules(r?.modules || []);
        } catch (e) { notify.error('Erreur'); }
        finally { setLoading(false); }
    }, [notify]);

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

    const openNew = () => {
        setEditing(null);
        setForm({
            code: '', name: '', description: '', category: 'general', icon: '',
            price_chf: 0, setup_fee_chf: 0, billing_period: 'month',
            is_active: 1, is_default: 0, trial_days: 0, sort_order: 0,
        });
        setShowModal(true);
    };
    const openEdit = (m) => {
        setEditing(m);
        setForm({
            code: m.code, name: m.name, description: m.description || '',
            category: m.category || 'general', icon: m.icon || '',
            price_chf: m.price_chf || 0, setup_fee_chf: m.setup_fee_chf || 0,
            billing_period: m.billing_period || 'month',
            is_active: m.is_active ? 1 : 0, is_default: m.is_default ? 1 : 0,
            trial_days: m.trial_days || 0, sort_order: m.sort_order || 0,
        });
        setShowModal(true);
    };

    const save = async () => {
        if (!form.code.trim() || !form.name.trim()) return notify.error('code et name requis');
        try {
            if (editing) {
                await api.put(`/api/admin/modules/${editing.id}`, form);
                notify.success('Module mis à jour');
            } else {
                const r = await api.post('/api/admin/modules', form);
                if (r?.error) return notify.error(r.error);
                notify.success('Module créé');
            }
            setShowModal(false);
            load();
        } catch (e) { notify.error('Erreur: ' + e.message); }
    };

    const remove = async (m) => {
        if (!confirm(`Désactiver le module "${m.name}" ? (les souscriptions actives ne sont pas affectées)`)) return;
        await api.del(`/api/admin/modules/${m.id}`);
        notify.success('Module désactivé');
        load();
    };

    const viewSubs = async (m) => {
        setShowSubs(m);
        try {
            const r = await api.get(`/api/admin/modules/${m.id}/subscribers`);
            setSubs(r?.subscribers || []);
        } catch (e) { setSubs([]); }
    };

    const totalRevenue = modules.reduce((s, m) => s + (m.monthly_revenue || 0), 0);
    const totalSubscribers = modules.reduce((s, m) => s + (m.active_subscribers || 0), 0);

    return (
        <>
            <div className="page-header">
                <div>
                    <h1 className="page-title">Modules</h1>
                    <p className="page-subtitle">Catalogue d'options activables par les clients (gratuites ou payantes).</p>
                </div>
                <button className="btn btn-primary" onClick={openNew}><Icons.Plus className="w-4 h-4" /> Nouveau module</button>
            </div>
            <div className="page-content">
                <div className="stats-grid" style={{ marginBottom: '1rem' }}>
                    <div className="stat-card"><div className="stat-label">Modules</div><div className="stat-value">{modules.length}</div></div>
                    <div className="stat-card"><div className="stat-label">Souscriptions actives</div><div className="stat-value">{totalSubscribers}</div></div>
                    <div className="stat-card"><div className="stat-label">Revenu mensuel projeté</div><div className="stat-value">{totalRevenue.toFixed(2)} CHF</div></div>
                </div>

                {loading ? <SkeletonCards count={3} columns={1} /> : (
                    <div className="data-table-container">
                        <table className="data-table">
                            <thead><tr>
                                <th>Code</th>
                                <th>Nom</th>
                                <th>Catégorie</th>
                                <th>Prix</th>
                                <th>Période</th>
                                <th>Essai</th>
                                <th>Actifs</th>
                                <th>Revenu/mois</th>
                                <th>Total perçu</th>
                                <th>Statut</th>
                                <th>Actions</th>
                            </tr></thead>
                            <tbody>
                                {modules.map(m => (
                                    <tr key={m.id}>
                                        <td><code>{m.code}</code></td>
                                        <td><strong>{m.name}</strong>{m.is_default ? <span style={{ marginLeft: 6, fontSize: '0.7rem', background: '#dbeafe', color: '#1e40af', padding: '0.1rem 0.4rem', borderRadius: 4 }}>défaut</span> : null}</td>
                                        <td>{m.category}</td>
                                        <td>
                                            <strong>{(m.price_chf || 0).toFixed(2)} CHF</strong>
                                            {m.setup_fee_chf > 0 && <div style={{ fontSize: '0.7rem', color: '#64748b' }}>+ {m.setup_fee_chf.toFixed(2)} setup</div>}
                                        </td>
                                        <td>{m.billing_period}</td>
                                        <td>{m.trial_days > 0 ? `${m.trial_days}j` : '-'}</td>
                                        <td>
                                            <button className="btn btn-secondary btn-sm" onClick={() => viewSubs(m)}>{m.active_subscribers || 0}</button>
                                        </td>
                                        <td>{(m.monthly_revenue || 0).toFixed(2)} CHF</td>
                                        <td>{(m.total_collected || 0).toFixed(2)} CHF</td>
                                        <td>{m.is_active ? <span style={{ color: '#16a34a' }}>● Actif</span> : <span style={{ color: '#94a3b8' }}>○ Inactif</span>}</td>
                                        <td>
                                            <button className="btn btn-secondary btn-sm" onClick={() => openEdit(m)}>Éditer</button>
                                            <button className="btn btn-secondary btn-sm" onClick={() => remove(m)} style={{ color: '#dc2626', marginLeft: 4 }}>×</button>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {showModal && (
                <div className="modal-overlay" onClick={() => setShowModal(false)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 600 }}>
                        <div className="modal-header">
                            <h2>{editing ? 'Modifier le module' : 'Nouveau module'}</h2>
                            <button className="modal-close" onClick={() => setShowModal(false)}>×</button>
                        </div>
                        <div className="modal-body" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '0.75rem' }}>
                            <div className="form-group">
                                <label>Code (technique) *</label>
                                <input className="form-input" value={form.code} disabled={!!editing}
                                    onChange={e => setForm({ ...form, code: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '_') })}
                                    placeholder="ex: tts_studio" />
                            </div>
                            <div className="form-group">
                                <label>Nom affiché *</label>
                                <input className="form-input" value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} placeholder="Studio voix" />
                            </div>
                            <div className="form-group" style={{ gridColumn: '1 / -1' }}>
                                <label>Description</label>
                                <textarea className="form-input" rows={2} value={form.description} onChange={e => setForm({ ...form, description: e.target.value })} />
                            </div>
                            <div className="form-group">
                                <label>Catégorie</label>
                                <select className="form-input" value={form.category} onChange={e => setForm({ ...form, category: e.target.value })}>
                                    <option value="ai">IA</option>
                                    <option value="communication">Communication</option>
                                    <option value="crm">CRM</option>
                                    <option value="monitoring">Monitoring</option>
                                    <option value="general">Général</option>
                                </select>
                            </div>
                            <div className="form-group">
                                <label>Icône (nom React)</label>
                                <input className="form-input" value={form.icon} onChange={e => setForm({ ...form, icon: e.target.value })} placeholder="Sparkles" />
                            </div>
                            <div className="form-group">
                                <label>Prix mensuel (CHF) *</label>
                                <input type="number" step="0.01" min="0" className="form-input" value={form.price_chf}
                                    onChange={e => setForm({ ...form, price_chf: parseFloat(e.target.value) || 0 })} />
                            </div>
                            <div className="form-group">
                                <label>Frais d'activation (CHF)</label>
                                <input type="number" step="0.01" min="0" className="form-input" value={form.setup_fee_chf}
                                    onChange={e => setForm({ ...form, setup_fee_chf: parseFloat(e.target.value) || 0 })} />
                            </div>
                            <div className="form-group">
                                <label>Période</label>
                                <select className="form-input" value={form.billing_period} onChange={e => setForm({ ...form, billing_period: e.target.value })}>
                                    <option value="month">Mensuel</option>
                                    <option value="year">Annuel</option>
                                    <option value="one-time">Paiement unique</option>
                                </select>
                            </div>
                            <div className="form-group">
                                <label>Essai (jours)</label>
                                <input type="number" min="0" className="form-input" value={form.trial_days}
                                    onChange={e => setForm({ ...form, trial_days: parseInt(e.target.value) || 0 })} />
                            </div>
                            <div className="form-group">
                                <label>Ordre d'affichage</label>
                                <input type="number" className="form-input" value={form.sort_order}
                                    onChange={e => setForm({ ...form, sort_order: parseInt(e.target.value) || 0 })} />
                            </div>
                            <div className="form-group">
                                <label><input type="checkbox" checked={!!form.is_active} onChange={e => setForm({ ...form, is_active: e.target.checked ? 1 : 0 })} /> Visible dans le catalogue</label>
                            </div>
                            <div className="form-group">
                                <label><input type="checkbox" checked={!!form.is_default} onChange={e => setForm({ ...form, is_default: e.target.checked ? 1 : 0 })} /> Activé par défaut</label>
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowModal(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={save}>Enregistrer</button>
                        </div>
                    </div>
                </div>
            )}

            {showSubs && (
                <div className="modal-overlay" onClick={() => setShowSubs(null)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 720 }}>
                        <div className="modal-header">
                            <h2>Souscripteurs : {showSubs.name}</h2>
                            <button className="modal-close" onClick={() => setShowSubs(null)}>×</button>
                        </div>
                        <div className="modal-body">
                            {subs.length === 0 ? <p>Aucune souscription.</p> : (
                                <table className="data-table">
                                    <thead><tr><th>Client</th><th>Statut</th><th>Activé le</th><th>Expire</th><th>Total payé</th></tr></thead>
                                    <tbody>{subs.map(s => (
                                        <tr key={s.id}>
                                            <td>{s.client_name}<br/><small>{s.client_email}</small></td>
                                            <td>{s.status}</td>
                                            <td>{new Date(s.activated_at).toLocaleDateString('fr-CH')}</td>
                                            <td>{s.expires_at ? new Date(s.expires_at).toLocaleDateString('fr-CH') : '-'}</td>
                                            <td>{(s.total_paid_chf || 0).toFixed(2)} CHF</td>
                                        </tr>
                                    ))}</tbody>
                                </table>
                            )}
                        </div>
                    </div>
                </div>
            )}
        </>
    );
};

// ==================== APP ROOT ====================
const App = () => {
    const { user, view, sidebarOpen } = useApp();

    if (!user) return <LoginScreen />;

    return (
        <div className="app-layout">
            <Sidebar />
            <div className="main-content">
                <MobileHeader />
                {view === 'dashboard' && <DashboardView />}
                {view === 'clients' && <ClientsView />}
                {view === 'client-detail' && <ClientDetailView />}
                {view === 'lines' && <LinesView />}
                {view === 'line-detail' && <LineDetailView />}
                {view === 'calls' && <CallsView />}
                {view === 'demandes' && <DemandesView />}
                {view === 'sms' && <SmsView />}
                {view === 'whatsapp' && <WhatsappView />}
                {view === 'subscriptions' && <SubscriptionsView />}
                {view === 'modules' && <ModulesAdminView />}
                {view === 'partners' && <PartnersView />}
                {view === 'api' && <ApiView />}
                {view === 'twilio' && <TwilioAccountsView />}
                {view === 'settings' && <SettingsView />}
                {view === 'access' && <AccessView />}
                {view === 'admins' && <AdminsView />}
                {view === 'analytics-platform' && <AnalyticsPlatformView />}
                {view === 'analytics' && <AnalyticsView />}
                {view === 'mcp-keys' && <McpKeysView />}
                {view === 'diagnostic' && <DiagnosticView />}
                {view === 'support' && <SupportView />}
            </div>
        </div>
    );
};

// ==================== MCP KEYS VIEW ====================
const McpKeysView = () => {
    const { notify } = useApp();
    const [keys, setKeys] = useState([]);
    const [usage, setUsage] = useState(null);
    const [loading, setLoading] = useState(true);
    const [showCreate, setShowCreate] = useState(false);
    const [newKey, setNewKey] = useState(null);
    const [showUsage, setShowUsage] = useState(null);
    const [usageData, setUsageData] = useState(null);
    const [form, setForm] = useState({
        label: '', scopes: ['read'], rate_limit_per_min: 120,
        expires_days: '', ip_allowlist: '',
    });

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const [k, u] = await Promise.all([
                mcpApi.get('/admin/keys'),
                mcpApi.get('/admin/usage/summary'),
            ]);
            setKeys(k?.keys || []);
            setUsage(u || null);
        } catch (e) { notify.error('Erreur de chargement MCP'); }
        finally { setLoading(false); }
    }, [notify]);

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

    const create = async () => {
        if (!form.label.trim()) return notify.error('Label requis');
        try {
            const body = {
                label: form.label.trim(),
                scopes: form.scopes,
                rate_limit_per_min: parseInt(form.rate_limit_per_min) || 120,
                expires_days: form.expires_days ? parseInt(form.expires_days) : null,
                ip_allowlist: form.ip_allowlist.trim() || null,
            };
            const r = await mcpApi.post('/admin/keys', body);
            if (r?.error) return notify.error(r.error);
            setNewKey(r);
            setShowCreate(false);
            setForm({ label: '', scopes: ['read'], rate_limit_per_min: 120, expires_days: '', ip_allowlist: '' });
            load();
        } catch (e) { notify.error('Erreur de création'); }
    };

    const revoke = async (id, label) => {
        if (!confirm(`Révoquer la clé "${label}" ? Cette action est immédiate et irréversible.`)) return;
        try {
            const r = await mcpApi.del(`/admin/keys/${id}`);
            if (r?.error) return notify.error(r.error);
            notify.success('Clé révoquée');
            load();
        } catch (e) { notify.error('Erreur'); }
    };

    const toggle = async (k) => {
        try {
            await mcpApi.patch(`/admin/keys/${k.id}`, { is_active: !k.is_active });
            load();
        } catch (e) { notify.error('Erreur'); }
    };

    const openUsage = async (k) => {
        setShowUsage(k);
        setUsageData(null);
        try {
            const r = await mcpApi.get(`/admin/keys/${k.id}/usage?limit=100`);
            setUsageData(r);
        } catch (e) { notify.error('Erreur'); }
    };

    const copy = async (text, label = 'Copié') => {
        try { await navigator.clipboard.writeText(text); notify.success(label); }
        catch (e) { notify.error('Copie impossible'); }
    };

    const fmtScope = (s) => {
        const colors = { read: '#0891b2', write: '#d97706', admin: '#dc2626' };
        return s.split(',').filter(Boolean).map(x => (
            <span key={x} style={{
                display: 'inline-block', padding: '2px 8px', borderRadius: 999,
                background: (colors[x.trim()] || '#64748b') + '20',
                color: colors[x.trim()] || '#64748b',
                fontSize: '0.7rem', fontWeight: 700, marginRight: 4,
            }}>{x.trim()}</span>
        ));
    };

    const fmtDate = (d) => {
        if (!d) return '—';
        try {
            const dt = new Date(d.includes('T') ? d : d.replace(' ', 'T') + 'Z');
            return dt.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', year: 'numeric' })
                + ' ' + dt.toLocaleTimeString('fr-CH', { hour: '2-digit', minute: '2-digit' });
        } catch { return d; }
    };

    if (loading) return <div className="page-content"><div className="loading">Chargement…</div></div>;

    return (
        <div className="page-content">
            <div className="page-header">
                <div>
                    <h1 className="page-title">
                        <Icons.Code className="w-7 h-7" style={{ display: 'inline', verticalAlign: 'middle', marginRight: 8 }} />
                        MCP Keys
                    </h1>
                    <p className="page-subtitle">
                        Clés d'API pour le serveur MCP <code>mcp.helvia.app</code> — utilisées par les bots distants pour explorer la plateforme via <code>/nux</code>.
                    </p>
                </div>
                <button className="btn btn-primary" onClick={() => setShowCreate(true)}>
                    <Icons.Plus className="w-4 h-4" /> Nouvelle clé
                </button>
            </div>

            {/* KPI cards */}
            <div className="kpi-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem', marginBottom: '1.5rem' }}>
                <div className="kpi-card">
                    <div className="kpi-label">Clés actives</div>
                    <div className="kpi-value">{keys.filter(k => k.is_active).length}</div>
                    <div className="kpi-sub">{keys.length} au total</div>
                </div>
                <div className="kpi-card">
                    <div className="kpi-label">Appels (24h)</div>
                    <div className="kpi-value">{usage?.calls_24h || 0}</div>
                    <div className="kpi-sub">{usage?.calls_7d || 0} sur 7 jours</div>
                </div>
                <div className="kpi-card">
                    <div className="kpi-label">Erreurs (7j)</div>
                    <div className="kpi-value" style={{ color: (usage?.errors_7d || 0) > 0 ? '#dc2626' : '#16a34a' }}>
                        {usage?.errors_7d || 0}
                    </div>
                    <div className="kpi-sub">HTTP ≥ 400</div>
                </div>
                <div className="kpi-card">
                    <div className="kpi-label">Endpoint principal</div>
                    <div className="kpi-value" style={{ fontSize: '1.1rem' }}>
                        <code>GET /nux</code>
                    </div>
                    <div className="kpi-sub">
                        <a href="https://mcp.helvia.app/mcp/manifest" target="_blank" rel="noopener">Manifest →</a>
                    </div>
                </div>
            </div>

            {/* Quick start */}
            <div className="card" style={{ padding: '1rem', marginBottom: '1.5rem', background: 'linear-gradient(135deg, #f8fafc, #eef2ff)' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <Icons.Terminal className="w-6 h-6" style={{ color: '#6366f1' }} />
                    <div style={{ flex: 1 }}>
                        <div style={{ fontWeight: 600, marginBottom: 4 }}>Quick start pour votre bot</div>
                        <div style={{ fontSize: '0.85rem', color: '#475569', fontFamily: 'monospace' }}>
                            curl -H "Authorization: Bearer mcp_live_…" https://mcp.helvia.app/nux
                        </div>
                    </div>
                    <button className="btn btn-secondary btn-sm" onClick={() => copy('curl -H "Authorization: Bearer mcp_live_REPLACE" https://mcp.helvia.app/nux', 'Commande copiée')}>
                        <Icons.Copy className="w-4 h-4" /> Copier
                    </button>
                </div>
            </div>

            {/* Keys table */}
            <div className="card" style={{ overflow: 'hidden' }}>
                {keys.length === 0 ? (
                    <div style={{ padding: '3rem', textAlign: 'center', color: 'var(--text-muted)' }}>
                        <Icons.Key className="w-12 h-12" style={{ margin: '0 auto 1rem', opacity: 0.4 }} />
                        <div>Aucune clé MCP créée. Cliquez sur "Nouvelle clé" pour en générer une.</div>
                    </div>
                ) : (
                    <table className="data-table">
                        <thead>
                            <tr>
                                <th>Label</th>
                                <th>Préfixe</th>
                                <th>Scopes</th>
                                <th>Usage</th>
                                <th>Dernier appel</th>
                                <th>Expire</th>
                                <th>Statut</th>
                                <th></th>
                            </tr>
                        </thead>
                        <tbody>
                            {keys.map(k => (
                                <tr key={k.id} style={{ opacity: k.is_active ? 1 : 0.5 }}>
                                    <td>
                                        <div style={{ fontWeight: 600 }}>{k.label}</div>
                                        <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>#{k.id}</div>
                                    </td>
                                    <td><code style={{ fontSize: '0.8rem' }}>{k.key_prefix}…</code></td>
                                    <td>{fmtScope(k.scopes || 'read')}</td>
                                    <td>
                                        <div style={{ fontWeight: 600 }}>{k.use_count || 0}</div>
                                        <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>{k.rate_limit_per_min}/min</div>
                                    </td>
                                    <td style={{ fontSize: '0.8rem' }}>
                                        {fmtDate(k.last_used_at)}
                                        {k.last_used_ip && (
                                            <div style={{ fontSize: '0.7rem', color: 'var(--text-muted)' }}>{k.last_used_ip}</div>
                                        )}
                                    </td>
                                    <td style={{ fontSize: '0.8rem' }}>{k.expires_at ? fmtDate(k.expires_at) : 'jamais'}</td>
                                    <td>
                                        <span style={{
                                            padding: '2px 8px', borderRadius: 999, fontSize: '0.7rem', fontWeight: 700,
                                            background: k.is_active ? '#dcfce7' : '#fee2e2',
                                            color: k.is_active ? '#166534' : '#991b1b',
                                        }}>
                                            {k.is_active ? 'active' : 'révoquée'}
                                        </span>
                                    </td>
                                    <td style={{ display: 'flex', gap: 4 }}>
                                        <button className="btn btn-secondary btn-sm" title="Logs d'usage" onClick={() => openUsage(k)}>
                                            <Icons.Activity className="w-4 h-4" />
                                        </button>
                                        <button className="btn btn-secondary btn-sm" title={k.is_active ? 'Désactiver' : 'Réactiver'} onClick={() => toggle(k)}>
                                            {k.is_active ? <Icons.EyeOff className="w-4 h-4" /> : <Icons.Eye className="w-4 h-4" />}
                                        </button>
                                        <button className="btn btn-secondary btn-sm" title="Révoquer" onClick={() => revoke(k.id, k.label)} style={{ color: '#dc2626' }}>
                                            <Icons.Trash className="w-4 h-4" />
                                        </button>
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                )}
            </div>

            {/* Create modal */}
            {showCreate && (
                <div className="modal-overlay" onClick={() => setShowCreate(false)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 540 }}>
                        <div className="modal-header">
                            <h2><Icons.Plus className="w-5 h-5" /> Nouvelle clé MCP</h2>
                            <button onClick={() => setShowCreate(false)}><Icons.X className="w-5 h-5" /></button>
                        </div>
                        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                            <div>
                                <label>Label *</label>
                                <input type="text" className="form-input" value={form.label}
                                    onChange={e => setForm(f => ({ ...f, label: e.target.value }))}
                                    placeholder="Ex: Claude Desktop — Yannick" />
                            </div>
                            <div>
                                <label>Scopes (permissions)</label>
                                <div style={{ display: 'flex', gap: 12, marginTop: 6 }}>
                                    {['read', 'write', 'admin'].map(s => (
                                        <label key={s} style={{ display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }}>
                                            <input type="checkbox" checked={form.scopes.includes(s)}
                                                onChange={e => setForm(f => ({
                                                    ...f,
                                                    scopes: e.target.checked
                                                        ? [...f.scopes, s]
                                                        : f.scopes.filter(x => x !== s),
                                                }))} />
                                            <span style={{ fontSize: '0.85rem' }}>{s}</span>
                                        </label>
                                    ))}
                                </div>
                                <div style={{ fontSize: '0.75rem', color: 'var(--text-muted)', marginTop: 4 }}>
                                    <strong>read</strong> = tous les GET /nux/* · <strong>write</strong> = actions (credit, toggle, notify) · <strong>admin</strong> = tout
                                </div>
                            </div>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
                                <div>
                                    <label>Rate limit (req/min)</label>
                                    <input type="number" className="form-input" value={form.rate_limit_per_min}
                                        onChange={e => setForm(f => ({ ...f, rate_limit_per_min: e.target.value }))} />
                                </div>
                                <div>
                                    <label>Expire dans (jours)</label>
                                    <input type="number" className="form-input" value={form.expires_days}
                                        onChange={e => setForm(f => ({ ...f, expires_days: e.target.value }))}
                                        placeholder="vide = jamais" />
                                </div>
                            </div>
                            <div>
                                <label>IP allowlist (optionnel)</label>
                                <input type="text" className="form-input" value={form.ip_allowlist}
                                    onChange={e => setForm(f => ({ ...f, ip_allowlist: e.target.value }))}
                                    placeholder="ex: 1.2.3.4, 5.6.7.8" />
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-secondary" onClick={() => setShowCreate(false)}>Annuler</button>
                            <button className="btn btn-primary" onClick={create}>Générer la clé</button>
                        </div>
                    </div>
                </div>
            )}

            {/* New key reveal modal */}
            {newKey && (
                <div className="modal-overlay" onClick={() => setNewKey(null)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 600 }}>
                        <div className="modal-header" style={{ background: 'linear-gradient(135deg, #16a34a, #059669)', color: '#fff' }}>
                            <h2 style={{ color: '#fff' }}>✓ Clé créée — copiez-la maintenant</h2>
                        </div>
                        <div className="modal-body">
                            <div style={{
                                padding: '0.75rem 1rem',
                                background: '#fef3c7',
                                border: '1px solid #fbbf24',
                                borderRadius: 8,
                                marginBottom: '1rem',
                                fontSize: '0.85rem',
                                color: '#92400e',
                            }}>
                                ⚠️ Cette clé ne sera <strong>plus jamais affichée</strong>. Copiez-la dès maintenant et stockez-la en lieu sûr.
                            </div>
                            <label>Clé API</label>
                            <div style={{ display: 'flex', gap: 8, alignItems: 'stretch' }}>
                                <code style={{
                                    flex: 1, padding: '0.75rem',
                                    background: '#0f172a', color: '#67e8f9',
                                    borderRadius: 8, fontSize: '0.85rem', wordBreak: 'break-all',
                                }}>{newKey.key}</code>
                                <button className="btn btn-primary" onClick={() => copy(newKey.key, 'Clé copiée !')}>
                                    <Icons.Copy className="w-4 h-4" /> Copier
                                </button>
                            </div>
                            <div style={{ marginTop: '1rem', fontSize: '0.85rem', color: 'var(--text-muted)' }}>
                                <div><strong>Label :</strong> {newKey.label}</div>
                                <div><strong>Scopes :</strong> {newKey.scopes}</div>
                                <div><strong>Rate limit :</strong> {newKey.rate_limit_per_min} req/min</div>
                                {newKey.expires_at && <div><strong>Expire :</strong> {fmtDate(newKey.expires_at)}</div>}
                            </div>
                            <div style={{ marginTop: '1rem' }}>
                                <label>Exemple curl</label>
                                <code style={{
                                    display: 'block', padding: '0.75rem', background: '#f1f5f9',
                                    borderRadius: 8, fontSize: '0.78rem', wordBreak: 'break-all',
                                }}>
                                    curl -H "Authorization: Bearer {newKey.key}" https://mcp.helvia.app/nux
                                </code>
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button className="btn btn-primary" onClick={() => setNewKey(null)}>J'ai copié la clé</button>
                        </div>
                    </div>
                </div>
            )}

            {/* Usage modal */}
            {showUsage && (
                <div className="modal-overlay" onClick={() => setShowUsage(null)}>
                    <div className="modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 800 }}>
                        <div className="modal-header">
                            <h2>Usage — {showUsage.label}</h2>
                            <button onClick={() => setShowUsage(null)}><Icons.X className="w-5 h-5" /></button>
                        </div>
                        <div className="modal-body">
                            {!usageData ? <div className="loading">Chargement…</div> : (
                                <>
                                    {usageData.by_path?.length > 0 && (
                                        <>
                                            <h4 style={{ marginTop: 0 }}>Top endpoints (7j)</h4>
                                            <table className="data-table" style={{ marginBottom: '1.5rem' }}>
                                                <thead><tr><th>Endpoint</th><th style={{ textAlign: 'right' }}>Appels</th></tr></thead>
                                                <tbody>
                                                    {usageData.by_path.map((p, i) => (
                                                        <tr key={i}>
                                                            <td><code style={{ fontSize: '0.78rem' }}>{p.path}</code></td>
                                                            <td style={{ textAlign: 'right', fontWeight: 600 }}>{p.n}</td>
                                                        </tr>
                                                    ))}
                                                </tbody>
                                            </table>
                                        </>
                                    )}
                                    <h4>Logs récents</h4>
                                    <div style={{ maxHeight: 360, overflow: 'auto' }}>
                                        <table className="data-table">
                                            <thead><tr><th>Date</th><th>Méthode</th><th>Path</th><th>Status</th><th>Latence</th><th>IP</th></tr></thead>
                                            <tbody>
                                                {(usageData.recent || []).map(l => (
                                                    <tr key={l.id}>
                                                        <td style={{ fontSize: '0.75rem' }}>{fmtDate(l.created_at)}</td>
                                                        <td><code style={{ fontSize: '0.75rem' }}>{l.method}</code></td>
                                                        <td><code style={{ fontSize: '0.75rem' }}>{l.path}</code></td>
                                                        <td>
                                                            <span style={{
                                                                padding: '2px 6px', borderRadius: 4, fontSize: '0.7rem', fontWeight: 700,
                                                                background: l.status >= 400 ? '#fee2e2' : '#dcfce7',
                                                                color: l.status >= 400 ? '#991b1b' : '#166534',
                                                            }}>{l.status}</span>
                                                        </td>
                                                        <td style={{ fontSize: '0.75rem' }}>{l.duration_ms}ms</td>
                                                        <td style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{l.ip || '—'}</td>
                                                    </tr>
                                                ))}
                                            </tbody>
                                        </table>
                                    </div>
                                </>
                            )}
                        </div>
                    </div>
                </div>
            )}
        </div>
    );
};

// Mount
ReactDOM.createRoot(document.getElementById('app')).render(
    <AppProvider><App /></AppProvider>
);
