// Vocal - Lines Management App
const { useState, useEffect, useRef, useCallback, useMemo, createContext, useContext } = React;

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

const LINE_TYPES = {
    forward: { label: 'Redirection', iconComponent: 'ArrowUpRight', color: 'type-forward' },
    ivr: { label: 'Menu IVR', iconComponent: 'Phone', color: 'type-ivr' },
    voice_ai: { label: 'IA Vocale', iconComponent: 'Robot', color: 'type-voice_ai' },
    voicemail: { label: 'Messagerie', iconComponent: 'Inbox', color: 'type-voicemail' },
    browser: { label: 'Navigateur', iconComponent: 'Globe', color: 'type-browser' },
};

const STATUS_MAP = {
    available: { label: 'Disponible', class: 'badge-success' },
    reserved: { label: 'Réservé', class: 'badge-warning' },
    assigned: { label: 'Attribué', class: 'badge-info' },
};

const CALL_STATUS = {
    completed: { label: 'Terminé', class: 'badge-success' },
    ringing: { label: 'Sonne', class: 'badge-info' },
    'in-progress': { label: 'En cours', class: 'badge-info' },
    busy: { label: 'Occupé', class: 'badge-warning' },
    'no-answer': { label: 'Sans réponse', class: 'badge-warning' },
    failed: { label: 'Échoué', class: 'badge-danger' },
    canceled: { label: 'Annulé', class: 'badge-gray' },
};

const VOICES = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'];

function formatDuration(seconds) {
    if (!seconds) return '0s';
    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' : ''}`;
}

function formatPhone(num) {
    if (!num) return '-';
    return num;
}

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

function formatDateTime(dateStr) {
    if (!dateStr) return '-';
    const d = new Date(dateStr);
    return d.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
}

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

// Icons
const Icons = {
    Wallet: ({ className = "w-6 h-6" }) => (
        <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 10a4 4 0 014-4h10a4 4 0 014 4v2h-3a3 3 0 100 6h3v2a2 2 0 01-2 2H5a2 2 0 01-2-2V10zm14 4a1 1 0 110-2 1 1 0 010 2z" />
        </svg>
    ),
    MessageCircle: ({ className = "w-6 h-6" }) => (
        <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 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" />
        </svg>
    ),
    Backspace: ({ className = "w-6 h-6" }) => (
        <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 12l5-7h13v14H8l-5-7zm9-3l5 5m0-5l-5 5" />
        </svg>
    ),
    Card: ({ className = "w-6 h-6" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <rect x="2" y="5" width="20" height="14" rx="2" />
            <path strokeLinecap="round" d="M2 10h20" />
        </svg>
    ),
    Globe2: ({ className = "w-6 h-6" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
            <circle cx="12" cy="12" r="9" />
            <path d="M3 12h18M12 3a14 14 0 010 18M12 3a14 14 0 000 18" />
        </svg>
    ),
    Phone: ({ className = "w-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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>
    ),
    BarChart: ({ className = "w-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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>
    ),
    LogOut: ({ className = "w-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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>
    ),
    Shield: ({ className = "w-6 h-6" }) => (
        <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>
    ),
    Refresh: ({ className = "w-6 h-6" }) => (
        <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>
    ),
    Edit: ({ className = "w-6 h-6" }) => (
        <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-6 h-6" }) => (
        <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>
    ),
    Volume: ({ className = "w-6 h-6" }) => (
        <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.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
        </svg>
    ),
    User: ({ className = "w-6 h-6" }) => (
        <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 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
        </svg>
    ),
    Mic: ({ className = "w-6 h-6" }) => (
        <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 0h4M12 15a3 3 0 003-3V5a3 3 0 00-6 0v7a3 3 0 003 3z" />
        </svg>
    ),
    MicOff: ({ className = "w-6 h-6" }) => (
        <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.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2" />
        </svg>
    ),
    PhoneOff: ({ className = "w-6 h-6" }) => (
        <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 8l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M5 3a2 2 0 00-2 2v1c0 8.284 6.716 15 15 15h1a2 2 0 002-2v-3.28a1 1 0 00-.684-.948l-4.493-1.498a1 1 0 00-1.21.502l-1.13 2.257a11.042 11.042 0 01-5.516-5.516l2.257-1.13a1 1 0 00.502-1.21L9.228 3.683A1 1 0 008.279 3H5z" />
        </svg>
    ),
    Headphones: ({ className = "w-6 h-6" }) => (
        <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 18v-6a9 9 0 0118 0v6M3 18a3 3 0 003 3h0a3 3 0 003-3v-2a3 3 0 00-3-3h0a3 3 0 00-3 3v2zM21 18a3 3 0 01-3 3h0a3 3 0 01-3-3v-2a3 3 0 013-3h0a3 3 0 013 3v2z" />
        </svg>
    ),
    ArrowUpRight: ({ className = "w-6 h-6" }) => (
        <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="M7 17L17 7M17 7H7M17 7v10" />
        </svg>
    ),
    Robot: ({ className = "w-6 h-6" }) => (
        <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.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-1.232 1.643a1.5 1.5 0 01-1.2.607H7.432a1.5 1.5 0 01-1.2-.607L5 14.5m14 0V5a2 2 0 00-2-2H7a2 2 0 00-2 2v9.5M9 21h6m-6 0v-3m6 3v-3" />
        </svg>
    ),
    Globe: ({ className = "w-6 h-6" }) => (
        <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 008.716-6.747M12 21a9.004 9.004 0 01-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 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5a17.92 17.92 0 01-8.716-2.247m0 0A8.966 8.966 0 013 12c0-1.777.514-3.434 1.401-4.832" />
        </svg>
    ),
    Inbox: ({ className = "w-6 h-6" }) => (
        <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="M2.25 13.5h3.86a2.25 2.25 0 012.012 1.244l.256.512a2.25 2.25 0 002.013 1.244h3.218a2.25 2.25 0 002.013-1.244l.256-.512a2.25 2.25 0 012.013-1.244h3.859M12 3v8.25m0 0l-3-3m3 3l3-3M2.25 18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V6a2.25 2.25 0 00-2.25-2.25h-15A2.25 2.25 0 002.25 6v12z" />
        </svg>
    ),
    ShoppingCart: ({ className = "w-6 h-6" }) => (
        <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="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 00-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 00-16.536-1.84M7.5 14.25L5.106 5.272M6 20.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm12.75 0a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
        </svg>
    ),
    Signal: ({ className = "w-6 h-6" }) => (
        <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.348 14.652a3.75 3.75 0 010-5.304m5.304 0a3.75 3.75 0 010 5.304m-7.425 2.121a6.75 6.75 0 010-9.546m9.546 0a6.75 6.75 0 010 9.546M5.106 18.894c-3.808-3.807-3.808-9.98 0-13.788m13.788 0c3.808 3.808 3.808 9.98 0 13.788M12 12h.008v.008H12V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
        </svg>
    ),
    AlertTriangle: ({ className = "w-6 h-6" }) => (
        <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 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
        </svg>
    ),
    CheckCircle: ({ className = "w-6 h-6" }) => (
        <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 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
        </svg>
    ),
};

function LineTypeIcon({ type, className = "w-4 h-4" }) {
    const info = LINE_TYPES[type];
    if (!info) return null;
    const IC = Icons[info.iconComponent];
    return IC ? <IC className={className} /> : null;
}

// Hooks
const useNotifications = () => {
    const show = useCallback((message, type = 'info') => {
        const el = document.createElement('div');
        el.className = `notification ${type}`;
        el.innerHTML = `<div style="flex:1">${message}</div>`;
        document.body.appendChild(el);
        setTimeout(() => { el.classList.add('slide-out'); 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);

// API Service
const api = {
    token: null,
    onUnauthorized: null,
    async req(path, options = {}) {
        if (!this.token && path !== '/auth/logout') {
            return { error: 'Non authentifié' };
        }
        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 });
        if (resp.status === 401 && this.token && this.onUnauthorized) {
            this.onUnauthorized();
            return { error: 'Session expirée' };
        }
        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' }); },
};

// App Provider
const AppProvider = ({ children }) => {
    const [user, setUser] = useState(null);
    const [ready, setReady] = useState(false);
    const [view, setView] = useState('myline');
    const [stats, setStats] = useState(null);
    const [lines, setLines] = useState([]);
    const [calls, setCalls] = useState({ calls: [], total: 0, page: 1 });
    const [twilioAccounts, setTwilioAccounts] = useState([]);
    const [blacklist, setBlacklist] = useState([]);
    const [loading, setLoading] = useState(false);
    const [selectedLine, setSelectedLine] = useState(null);
    const [filteredLineId, setFilteredLineId] = useState(null);

    const notify = useNotifications();

    const clearSession = useCallback(() => {
        api.token = null;
        setUser(null);
        localStorage.removeItem('vocal_user');
        localStorage.removeItem('vocal_token');
    }, []);

    useEffect(() => {
        const saved = localStorage.getItem('vocal_user');
        const token = localStorage.getItem('vocal_token');
        if (!saved || !token) { setReady(true); return; }
        api.token = token;
        fetch(`${CONFIG.API_URL}/auth/me`, {
            headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
        }).then(resp => {
            if (!resp.ok) throw new Error('invalid');
            return resp.json();
        }).then(data => {
            if (data.success && data.user) {
                setUser({ ...data.user, token });
            } else {
                clearSession();
            }
        }).catch(() => {
            clearSession();
        }).finally(() => setReady(true));
    }, []);

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

    const logout = useCallback((silent = false) => {
        api.post('/auth/logout').catch(() => {});
        clearSession();
        if (!silent) notify.info('Déconnexion réussie');
    }, [notify, clearSession]);

    api.onUnauthorized = () => {
        clearSession();
        notify.error('Session expirée, veuillez vous reconnecter');
    };

    const navigate = useCallback((v) => { setView(v); setSelectedLine(null); }, []);

    const loadStats = useCallback(async () => {
        if (!api.token) return;
        try { const data = await api.get('/my/calls/daily-stats'); if (data && !data.error) setStats(data); }
        catch (e) { console.error(e); }
    }, []);

    const loadLines = useCallback(async () => {
        if (!api.token) return;
        setLoading(true);
        try { const data = await api.get('/my/lines'); if (data?.lines) setLines(data.lines); }
        catch (e) { notify.error('Erreur chargement des lignes'); }
        finally { setLoading(false); }
    }, [notify]);

    const loadCalls = useCallback(async (page = 1, lineId = null) => {
        if (!api.token) return;
        setLoading(true);
        try {
            let url = `/my/calls?page=${page}&limit=30`;
            if (lineId) url += `&line_id=${lineId}`;
            const data = await api.get(url);
            if (data && !data.error) setCalls(data);
        } catch (e) { notify.error('Erreur chargement des appels'); }
        finally { setLoading(false); }
    }, [notify]);

    const loadTwilioAccounts = useCallback(async () => {
        if (!api.token) return;
        try { const data = await api.get('/api/twilio-accounts'); setTwilioAccounts(Array.isArray(data) ? data : []); }
        catch (e) { console.error(e); }
    }, []);

    const loadBlacklist = useCallback(async () => {
        if (!api.token) return;
        try { const data = await api.get('/api/blacklist'); setBlacklist(Array.isArray(data) ? data : []); }
        catch (e) { console.error(e); }
    }, []);

    useEffect(() => {
        if (user && ready) {
            loadStats();
            loadLines();
        }
    }, [user, ready]);

    const filteredLine = useMemo(() => lines.find(l => l.id === filteredLineId) || null, [lines, filteredLineId]);

    const value = useMemo(() => ({
        user, ready, view, stats, lines, calls, twilioAccounts, blacklist, loading, selectedLine,
        filteredLineId, filteredLine, setFilteredLineId,
        login, logout, navigate, setSelectedLine, notify,
        loadStats, loadLines, loadCalls, loadTwilioAccounts, loadBlacklist,
        setLines, setCalls, setTwilioAccounts, setBlacklist,
    }), [user, ready, view, stats, lines, calls, twilioAccounts, blacklist, loading, selectedLine,
         filteredLineId, filteredLine,
         login, logout, navigate, notify, loadStats, loadLines, loadCalls, loadTwilioAccounts, loadBlacklist]);

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

// ==================== VOICE ====================

const VoiceContext = createContext();
const useVoice = () => useContext(VoiceContext);

const VOICE_STATUS = {
    offline: { label: 'Hors ligne', class: 'voice-offline' },
    connecting: { label: 'Connexion...', class: 'voice-connecting' },
    ready: { label: 'Prêt', class: 'voice-ready' },
    ringing: { label: 'Appel entrant', class: 'voice-ringing' },
    'in-call': { label: 'En appel', class: 'voice-incall' },
    error: { label: 'Erreur', class: 'voice-error' },
};

const VoiceProvider = ({ children }) => {
    const { user, notify } = useApp();
    const [voiceStatus, setVoiceStatus] = useState('offline');
    const [incomingCall, setIncomingCall] = useState(null);
    const [activeCall, setActiveCall] = useState(null);
    const [isMuted, setIsMuted] = useState(false);
    const [callDuration, setCallDuration] = useState(0);
    const [voiceLogs, setVoiceLogs] = useState([]);
    const [voiceIdentity, setVoiceIdentity] = useState(null);
    const deviceRef = useRef(null);
    const timerRef = useRef(null);
    const callStartRef = useRef(null);

    const addLog = useCallback((type, message) => {
        const entry = { id: Date.now(), type, message, time: new Date().toLocaleTimeString('fr-CH') };
        setVoiceLogs(prev => [entry, ...prev].slice(0, 100));
    }, []);

    const cleanup = useCallback(() => {
        if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; }
        setActiveCall(null);
        setIncomingCall(null);
        setIsMuted(false);
        setCallDuration(0);
        callStartRef.current = null;
        setVoiceStatus(deviceRef.current ? 'ready' : 'offline');
    }, []);

    const startTimer = useCallback(() => {
        callStartRef.current = Date.now();
        setCallDuration(0);
        timerRef.current = setInterval(() => {
            setCallDuration(Math.floor((Date.now() - callStartRef.current) / 1000));
        }, 1000);
    }, []);

    const initVoice = useCallback(async () => {
        if (!user || !window.Twilio) {
            addLog('error', 'SDK Twilio non disponible');
            return;
        }

        setVoiceStatus('connecting');
        addLog('info', 'Demande du token...');

        try {
            const data = await api.get('/voice/token');
            if (data.error) throw new Error(data.error);

            addLog('info', `Token reçu, identité: ${data.identity}`);
            setVoiceIdentity(data.identity);

            if (deviceRef.current) {
                deviceRef.current.destroy();
                deviceRef.current = null;
            }

            const device = new Twilio.Device(data.token, {
                logLevel: 1,
                codecPreferences: ['opus', 'pcmu'],
                closeProtection: 'Un appel est en cours. Voulez-vous quitter ?',
                edge: 'dublin',
            });

            device.on('registered', () => {
                setVoiceStatus('ready');
                addLog('success', 'Device enregistré - prêt à recevoir');
            });

            device.on('unregistered', () => {
                setVoiceStatus('offline');
                addLog('info', 'Device désenregistré');
            });

            device.on('error', (twilioError) => {
                setVoiceStatus('error');
                const msg = twilioError?.message || twilioError?.twilioError?.message || String(twilioError) || 'Erreur inconnue';
                addLog('error', `Erreur: ${msg} (code: ${twilioError?.code || '-'})`);
                notify.error(`Erreur voix: ${msg}`);
            });

            device.on('tokenWillExpire', async () => {
                addLog('info', 'Token bientôt expiré, renouvellement...');
                try {
                    const refreshData = await api.get('/voice/token');
                    if (refreshData.token) {
                        device.updateToken(refreshData.token);
                        addLog('success', 'Token renouvelé');
                    }
                } catch (e) {
                    addLog('error', `Erreur renouvellement token: ${e.message}`);
                }
            });

            device.on('incoming', (call) => {
                const from = call.parameters?.From || 'Inconnu';
                const callSid = call.parameters?.CallSid || '';
                addLog('info', `Appel entrant de ${from} (${callSid})`);
                setVoiceStatus('ringing');
                setIncomingCall({ call, from, callSid, time: new Date() });
                notify.info(`Appel entrant de ${from}`);

                call.on('cancel', () => {
                    addLog('info', 'Appel annulé par l\'appelant');
                    cleanup();
                });

                call.on('disconnect', () => {
                    addLog('info', 'Appel terminé');
                    cleanup();
                });

                call.on('reject', () => {
                    addLog('info', 'Appel refusé');
                    cleanup();
                });

                call.on('error', (err) => {
                    addLog('error', `Erreur appel: ${err.message}`);
                    cleanup();
                });
            });

            await device.register();
            deviceRef.current = device;
        } catch (e) {
            setVoiceStatus('error');
            addLog('error', `Init échouée: ${e.message}`);
            notify.error(`Impossible d'initialiser la voix: ${e.message}`);
        }
    }, [user, notify, addLog, cleanup]);

    const acceptCall = useCallback(() => {
        if (!incomingCall?.call) return;
        addLog('success', 'Appel accepté');
        incomingCall.call.accept();
        setActiveCall(incomingCall);
        setIncomingCall(null);
        setVoiceStatus('in-call');
        startTimer();
    }, [incomingCall, addLog, startTimer]);

    const makeCall = useCallback(async (to) => {
        if (!to) return { error: 'Numéro requis' };
        if (!deviceRef.current || voiceStatus === 'offline' || voiceStatus === 'error') {
            addLog('info', 'Device non prêt, initialisation...');
            await initVoice();
        }
        if (!deviceRef.current) {
            return { error: 'Voice SDK indisponible' };
        }
        try {
            addLog('info', `Appel sortant vers ${to}`);
            const call = await deviceRef.current.connect({ params: { To: to } });
            const wrapped = { call, from: to, to, time: new Date(), outgoing: true };
            setActiveCall(wrapped);
            setVoiceStatus('in-call');
            startTimer();
            call.on('disconnect', () => { addLog('info', 'Appel terminé'); cleanup(); });
            call.on('cancel', () => { addLog('info', 'Appel annulé'); cleanup(); });
            call.on('error', (err) => { addLog('error', `Erreur: ${err.message}`); cleanup(); });
            call.on('accept', () => { addLog('success', 'Appel connecté'); });
            return { success: true };
        } catch (e) {
            addLog('error', `Appel échoué: ${e.message}`);
            return { error: e.message };
        }
    }, [voiceStatus, initVoice, addLog, startTimer, cleanup]);

    const rejectCall = useCallback(() => {
        if (!incomingCall?.call) return;
        addLog('info', 'Appel refusé manuellement');
        incomingCall.call.reject();
        cleanup();
    }, [incomingCall, addLog, cleanup]);

    const hangup = useCallback(() => {
        if (activeCall?.call) {
            addLog('info', 'Raccroché');
            activeCall.call.disconnect();
        }
        cleanup();
    }, [activeCall, addLog, cleanup]);

    const toggleMute = useCallback(() => {
        if (!activeCall?.call) return;
        const newMute = !isMuted;
        activeCall.call.mute(newMute);
        setIsMuted(newMute);
        addLog('info', newMute ? 'Micro coupé' : 'Micro activé');
    }, [activeCall, isMuted, addLog]);

    const destroyVoice = useCallback(() => {
        if (deviceRef.current) {
            deviceRef.current.destroy();
            deviceRef.current = null;
        }
        cleanup();
        setVoiceStatus('offline');
        setVoiceLogs([]);
        addLog('info', 'SDK détruit');
    }, [cleanup, addLog]);

    useEffect(() => {
        return () => {
            if (deviceRef.current) deviceRef.current.destroy();
            if (timerRef.current) clearInterval(timerRef.current);
        };
    }, []);

    const value = useMemo(() => ({
        voiceStatus, incomingCall, activeCall, isMuted, callDuration, voiceLogs, voiceIdentity,
        initVoice, acceptCall, rejectCall, hangup, toggleMute, destroyVoice, addLog, makeCall,
    }), [voiceStatus, incomingCall, activeCall, isMuted, callDuration, voiceLogs, voiceIdentity,
         initVoice, acceptCall, rejectCall, hangup, toggleMute, destroyVoice, addLog, makeCall]);

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

// ==================== VOICE UI COMPONENTS ====================

const VoiceStatusDot = () => {
    const { voiceStatus } = useVoice();
    const info = VOICE_STATUS[voiceStatus] || VOICE_STATUS.offline;
    return (
        <div className={`voice-dot ${info.class}`} title={info.label}>
            {voiceStatus === 'ringing' && <span className="voice-dot-ping"></span>}
        </div>
    );
};

const IncomingCallOverlay = () => {
    const { incomingCall, acceptCall, rejectCall } = useVoice();
    if (!incomingCall) return null;

    return (
        <div className="voice-overlay">
            <div className="voice-overlay-content">
                <div className="voice-overlay-pulse"></div>
                <div className="voice-caller-icon">
                    <Icons.PhoneIncoming className="w-10 h-10 text-white" />
                </div>
                <h2 className="text-xl font-bold text-white mt-6">Appel entrant</h2>
                <p className="text-2xl font-extrabold text-white mt-2">{incomingCall.from}</p>
                <p className="text-white/60 text-sm mt-1">{new Date(incomingCall.time).toLocaleTimeString('fr-CH')}</p>
                <div className="voice-call-actions mt-8">
                    <button onClick={rejectCall} className="voice-btn-reject" title="Refuser">
                        <Icons.PhoneOff className="w-7 h-7" />
                    </button>
                    <button onClick={acceptCall} className="voice-btn-accept" title="Répondre">
                        <Icons.Phone className="w-7 h-7" />
                    </button>
                </div>
            </div>
        </div>
    );
};

const ActiveCallBar = () => {
    const { activeCall, callDuration, isMuted, hangup, toggleMute } = useVoice();
    if (!activeCall) return null;

    const mins = Math.floor(callDuration / 60);
    const secs = callDuration % 60;
    const timeStr = `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;

    return (
        <div className="voice-active-bar">
            <div className="flex items-center gap-3 flex-1 min-w-0">
                <div className="voice-active-indicator"></div>
                <div className="min-w-0 flex-1">
                    <p className="text-sm font-bold text-white truncate">
                        {activeCall.outgoing ? '→ ' : '← '}{activeCall.from}
                    </p>
                    <p className="text-xs text-white/70">{timeStr}</p>
                </div>
            </div>
            <div className="flex items-center gap-2">
                <button onClick={toggleMute}
                    className={`voice-bar-btn ${isMuted ? 'voice-bar-btn-muted' : ''}`}
                    title={isMuted ? 'Activer micro' : 'Couper micro'}>
                    {isMuted ? <Icons.MicOff className="w-5 h-5" /> : <Icons.Mic className="w-5 h-5" />}
                </button>
                <button onClick={hangup} className="voice-bar-btn-hangup" title="Raccrocher">
                    <Icons.PhoneOff className="w-5 h-5" />
                </button>
            </div>
        </div>
    );
};

// ==================== VOICE TEST VIEW ====================

const VoiceTestView = () => {
    const { voiceStatus, voiceLogs, voiceIdentity, initVoice, destroyVoice, activeCall, incomingCall } = useVoice();
    const info = VOICE_STATUS[voiceStatus] || VOICE_STATUS.offline;

    return (
        <div className="p-4 pb-24 space-y-4 fade-in-up">
            <h1 className="text-2xl font-extrabold text-gray-900">Voice Lab</h1>
            <p className="text-sm text-gray-500">Test de réception d'appels dans le navigateur</p>

            <div className="card p-4 space-y-4">
                <div className="flex items-center justify-between">
                    <div>
                        <h3 className="font-bold text-gray-900">Statut SDK</h3>
                        <div className="flex items-center gap-2 mt-1">
                            <div className={`w-3 h-3 rounded-full ${info.class === 'voice-ready' ? 'bg-emerald-400' : info.class === 'voice-error' ? 'bg-red-400' : info.class === 'voice-connecting' ? 'bg-yellow-400 animate-pulse' : info.class === 'voice-ringing' ? 'bg-blue-400 animate-pulse' : info.class === 'voice-incall' ? 'bg-green-400' : 'bg-gray-300'}`}></div>
                            <span className="text-sm font-medium text-gray-700">{info.label}</span>
                        </div>
                        {voiceIdentity && <p className="text-xs text-gray-400 mt-1">Identité: {voiceIdentity}</p>}
                    </div>
                    {voiceStatus === 'offline' || voiceStatus === 'error' ? (
                        <button onClick={initVoice} className="btn-primary text-sm px-4 py-2">
                            Initialiser
                        </button>
                    ) : (
                        <button onClick={destroyVoice} className="btn-secondary text-sm px-4 py-2">
                            Déconnecter
                        </button>
                    )}
                </div>

                {(voiceStatus === 'ready' || voiceStatus === 'in-call') && (
                    <div className="bg-emerald-50 border border-emerald-200 rounded-lg p-3">
                        <p className="text-sm text-emerald-800 font-medium">
                            {voiceStatus === 'ready' ? 'En attente d\'appels entrants...' : 'Appel en cours'}
                        </p>
                        <p className="text-xs text-emerald-600 mt-1">
                            Appelez une ligne de type "Navigateur" pour tester
                        </p>
                    </div>
                )}

                {voiceStatus === 'error' && (
                    <div className="bg-red-50 border border-red-200 rounded-lg p-3">
                        <p className="text-sm text-red-800 font-medium">Erreur de connexion</p>
                        <p className="text-xs text-red-600 mt-1">
                            Vérifiez la configuration Twilio (API Key, Secret, TwiML App)
                        </p>
                    </div>
                )}
            </div>

            <div className="card p-4">
                <div className="flex items-center justify-between mb-3">
                    <h3 className="font-bold text-gray-900">Journal</h3>
                    <span className="text-xs text-gray-400">{voiceLogs.length} entrées</span>
                </div>
                <div className="space-y-1 max-h-[50vh] overflow-y-auto">
                    {voiceLogs.length === 0 ? (
                        <p className="text-sm text-gray-400 text-center py-4">Aucun événement</p>
                    ) : voiceLogs.map(log => (
                        <div key={log.id} className="flex items-start gap-2 text-xs py-1.5 border-b border-gray-50 last:border-0">
                            <span className={`w-2 h-2 rounded-full mt-1 flex-shrink-0 ${log.type === 'success' ? 'bg-emerald-400' : log.type === 'error' ? 'bg-red-400' : 'bg-blue-400'}`}></span>
                            <span className="text-gray-400 flex-shrink-0">{log.time}</span>
                            <span className="text-gray-700">{log.message}</span>
                        </div>
                    ))}
                </div>
            </div>

            <div className="card p-4">
                <h3 className="font-bold text-gray-900 mb-3">Checklist</h3>
                <div className="space-y-2">
                    {[
                        { ok: typeof window.Twilio !== 'undefined', label: 'SDK Twilio chargé' },
                        { ok: voiceStatus !== 'offline' && voiceStatus !== 'error', label: 'Device initialisé' },
                        { ok: voiceStatus === 'ready' || voiceStatus === 'in-call', label: 'Prêt à recevoir' },
                        { ok: !!navigator.mediaDevices, label: 'API Media disponible' },
                    ].map((item, i) => (
                        <div key={i} className="flex items-center gap-2">
                            <div className={`w-5 h-5 rounded flex items-center justify-center ${item.ok ? 'bg-emerald-100 text-emerald-600' : 'bg-gray-100 text-gray-400'}`}>
                                {item.ok ? <svg xmlns="http://www.w3.org/2000/svg" className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="3"><path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg> : <svg xmlns="http://www.w3.org/2000/svg" className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="3"><path strokeLinecap="round" strokeLinejoin="round" d="M18 12H6"/></svg>}
                            </div>
                            <span className={`text-sm ${item.ok ? 'text-gray-900' : 'text-gray-400'}`}>{item.label}</span>
                        </div>
                    ))}
                </div>
            </div>
        </div>
    );
};

// ==================== 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); }
    };

    return (
        <div className="min-h-screen flex items-center justify-center p-4" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
            <div className="w-full max-w-sm">
                <div className="text-center mb-8">
                    <h1 style={{ fontSize: '2.2rem', fontWeight: 800, color: 'white', letterSpacing: '-0.03em', margin: 0 }}>Vocal</h1>
                    <p className="text-white/80 mt-2">Gestion des lignes téléphoniques</p>
                </div>
                <div className="bg-white rounded-2xl p-6 shadow-2xl">
                    {step === 'email' ? (
                        <form onSubmit={handleSendCode}>
                            <h2 className="text-xl font-bold text-gray-900 mb-1">Connexion</h2>
                            <p className="text-gray-500 text-sm mb-6">Entrez votre email pour recevoir un code</p>
                            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)}
                                placeholder="votre@email.com" className="input-field mb-4" autoFocus />
                            {error && <p className="text-red-500 text-sm mb-4">{error}</p>}
                            <button type="submit" disabled={loading || !email} className="btn-primary w-full">
                                {loading ? 'Envoi...' : 'Recevoir le code'}
                            </button>
                        </form>
                    ) : (
                        <form onSubmit={handleVerify}>
                            <h2 className="text-xl font-bold text-gray-900 mb-1">Vérification</h2>
                            <p className="text-gray-500 text-sm mb-6">Code envoyé à {email}</p>
                            <input type="text" value={code} onChange={(e) => setCode(e.target.value)}
                                placeholder="123456" className="input-field mb-4 text-center text-2xl tracking-widest font-bold" maxLength="6" autoFocus />
                            {error && <p className="text-red-500 text-sm mb-4">{error}</p>}
                            <button type="submit" disabled={loading || code.length < 4} className="btn-primary w-full mb-3">
                                {loading ? 'Vérification...' : 'Vérifier'}
                            </button>
                            <button type="button" onClick={() => { setStep('email'); setCode(''); setError(''); }} className="btn-secondary w-full">
                                Retour
                            </button>
                        </form>
                    )}
                </div>
            </div>
        </div>
    );
};

// ==================== DASHBOARD ====================
const DashboardView = () => {
    const { stats, lines, loadStats, loadCalls, navigate, notify, filteredLineId, filteredLine } = useApp();
    const [syncing, setSyncing] = useState(false);

    const syncPricing = async () => {
        setSyncing(true);
        try {
            const data = await api.post('/api/calls/sync-pricing');
            notify.success(`${data.synced || 0} appels synchronisés`);
            loadStats();
        } catch (e) { notify.error('Erreur synchronisation'); }
        finally { setSyncing(false); }
    };

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

    const displayLines = filteredLineId ? lines.filter(l => l.id === filteredLineId) : lines;
    const activeLines = displayLines.filter(l => l.is_active);
    const typeCount = {};
    activeLines.forEach(l => { typeCount[l.type] = (typeCount[l.type] || 0) + 1; });

    return (
        <div className="p-4 pb-24 space-y-4 fade-in-up">
            <div className="flex items-center justify-between">
                <div>
                    <h1 className="text-2xl font-extrabold text-gray-900">Dashboard</h1>
                    {filteredLine && <p className="text-sm text-vocal font-medium">{formatPhone(filteredLine.phone_number)}</p>}
                </div>
                <button onClick={syncPricing} disabled={syncing} className="flex items-center gap-1 text-sm text-vocal font-medium">
                    <Icons.Refresh className={`w-4 h-4 ${syncing ? 'animate-spin' : ''}`} />
                    Sync
                </button>
            </div>

            {stats && (
                <>
                    <div className="grid grid-cols-2 gap-3">
                        <div className="stat-card stagger-item">
                            <div className="stat-value" style={{ color: '#6366f1' }}>{stats.active_lines}</div>
                            <div className="stat-label">Lignes actives</div>
                        </div>
                        <div className="stat-card stagger-item">
                            <div className="stat-value" style={{ color: '#10b981' }}>{stats.calls_today}</div>
                            <div className="stat-label">Appels aujourd'hui</div>
                        </div>
                        <div className="stat-card stagger-item">
                            <div className="stat-value" style={{ color: '#f59e0b' }}>{stats.calls_month}</div>
                            <div className="stat-label">Appels ce mois</div>
                        </div>
                        <div className="stat-card stagger-item">
                            <div className="stat-value" style={{ color: '#ef4444' }}>{formatCurrency(stats.cost_month)}</div>
                            <div className="stat-label">Coût ce mois</div>
                        </div>
                    </div>

                    <div className="grid grid-cols-2 gap-3">
                        <div className="stat-card">
                            <div className="stat-value text-lg">{formatDuration(stats.duration_today)}</div>
                            <div className="stat-label">Durée aujourd'hui</div>
                        </div>
                        <div className="stat-card">
                            <div className="stat-value text-lg">{stats.calls_total}</div>
                            <div className="stat-label">Total appels</div>
                        </div>
                    </div>
                </>
            )}

            <div className="card p-4">
                <div className="flex items-center justify-between mb-3">
                    <h3 className="font-bold text-gray-900">Répartition des lignes</h3>
                    <span className="text-sm text-gray-500">{activeLines.length} actives</span>
                </div>
                <div className="space-y-2">
                    {Object.entries(LINE_TYPES).map(([key, val]) => (
                        <div key={key} className="flex items-center justify-between py-1">
                            <div className="flex items-center gap-2">
                                <LineTypeIcon type={key} className="w-4 h-4" />
                                <span className="text-sm font-medium">{val.label}</span>
                            </div>
                            <span className="text-sm font-bold">{typeCount[key] || 0}</span>
                        </div>
                    ))}
                </div>
            </div>

            <DailyChart />

            <div className="grid grid-cols-2 gap-3">
                <button onClick={() => navigate('lines')} className="card p-4 text-left hover:border-vocal transition-all">
                    <Icons.Phone className="w-8 h-8 text-vocal mb-2" />
                    <div className="font-bold text-sm">Mes lignes</div>
                    <div className="text-xs text-gray-500">{lines.length} lignes</div>
                </button>
                <button onClick={() => { navigate('calls'); loadCalls(); }} className="card p-4 text-left hover:border-vocal transition-all">
                    <Icons.PhoneIncoming className="w-8 h-8 text-emerald-500 mb-2" />
                    <div className="font-bold text-sm">Appels</div>
                    <div className="text-xs text-gray-500">Historique</div>
                </button>
            </div>
        </div>
    );
};

// ==================== LINES ====================
const LinesView = () => {
    const { lines, loading, loadLines, navigate, setSelectedLine, twilioAccounts, notify } = useApp();
    const [search, setSearch] = useState('');
    const [showCreate, setShowCreate] = useState(false);
    const [filterType, setFilterType] = useState('all');

    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 || '').toLowerCase().includes(s) ||
                (l.label || '').toLowerCase().includes(s)
            );
        }
        return list;
    }, [lines, search, filterType]);

    const openLine = (line) => { setSelectedLine(line); navigate('line-detail'); };

    return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center justify-between">
                <h1 className="text-2xl font-extrabold text-gray-900">Lignes</h1>
                <button onClick={() => setShowCreate(true)} className="btn-primary flex items-center gap-1 text-sm py-2 px-4">
                    <Icons.Plus className="w-4 h-4" /> Nouvelle
                </button>
            </div>

            <div className="relative">
                <Icons.Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
                <input type="text" value={search} onChange={(e) => setSearch(e.target.value)}
                    placeholder="Rechercher..." className="input-field pl-10" />
            </div>

            <div className="tab-bar">
                <div className={`tab-item ${filterType === 'all' ? 'active' : ''}`} onClick={() => setFilterType('all')}>Toutes</div>
                {Object.entries(LINE_TYPES).map(([k, v]) => (
                    <div key={k} className={`tab-item ${filterType === k ? 'active' : ''}`} onClick={() => setFilterType(k)}><LineTypeIcon type={k} className="w-4 h-4" /></div>
                ))}
            </div>

            {loading && lines.length === 0 ? (
                <div className="flex justify-center py-12"><div className="spinner"></div></div>
            ) : filtered.length === 0 ? (
                <div className="empty-state">
                    <Icons.Phone className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucune ligne trouvée</p>
                    <p className="text-sm mt-1">Créez votre première ligne</p>
                </div>
            ) : (
                <div className="space-y-2">
                    {filtered.map((line, i) => (
                        <div key={line.id} className="line-card stagger-item" onClick={() => openLine(line)} style={{ animationDelay: `${i * 0.05}s` }}>
                            <div className="flex-1 min-w-0">
                                <div className="flex items-center gap-2 mb-1">
                                    <span className="font-bold text-sm">{formatPhone(line.phone_number)}</span>
                                    <span className={`type-badge ${LINE_TYPES[line.type]?.color || ''}`}>
                                        <LineTypeIcon type={line.type} className="w-3.5 h-3.5" /> {LINE_TYPES[line.type]?.label}
                                    </span>
                                </div>
                                <div className="text-sm text-gray-500 truncate">{line.description || line.label || 'Sans description'}</div>
                                <div className="flex items-center gap-3 mt-1.5">
                                    <span className="text-xs text-gray-400">{line.total_calls || 0} appels</span>
                                    <span className={`badge text-xs ${STATUS_MAP[line.line_status]?.class || 'badge-gray'}`}>
                                        {STATUS_MAP[line.line_status]?.label || line.line_status}
                                    </span>
                                </div>
                            </div>
                            <div className="flex items-center gap-2">
                                <div className={`w-2.5 h-2.5 rounded-full ${line.is_active ? 'bg-emerald-400' : 'bg-gray-300'}`}></div>
                                <Icons.ChevronRight className="w-5 h-5 text-gray-400" />
                            </div>
                        </div>
                    ))}
                </div>
            )}

            {showCreate && <CreateLineModal onClose={() => setShowCreate(false)} />}
        </div>
    );
};

// ==================== CREATE LINE MODAL ====================
const CreateLineModal = ({ onClose }) => {
    const { twilioAccounts, loadLines, notify } = useApp();
    const [form, setForm] = useState({
        phone_number: '', description: '', twilio_account_id: twilioAccounts[0]?.id || '',
        type: 'forward', forward_number: '', record_calls: 0, notification_number: '',
        caller_id_mode: 'caller', business_id: '', label: '',
        voice_instructions: '', voice_welcome_message: '', voice: 'alloy',
    });
    const [saving, setSaving] = useState(false);

    const update = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

    const handleSubmit = async (e) => {
        e.preventDefault();
        if (!form.phone_number || !form.twilio_account_id) {
            notify.error('Numéro et compte Twilio requis');
            return;
        }
        setSaving(true);
        try {
            const data = await api.post('/api/lines', form);
            if (data.success) { notify.success('Ligne créée'); loadLines(); onClose(); }
            else notify.error(data.error || 'Erreur');
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setSaving(false); }
    };

    return (
        <div className="modal" onClick={onClose}>
            <div className="modal-content" onClick={(e) => e.stopPropagation()}>
                <div className="flex items-center justify-between mb-4">
                    <h2 className="text-xl font-bold">Nouvelle ligne</h2>
                    <button onClick={onClose}><Icons.X className="w-6 h-6 text-gray-400" /></button>
                </div>
                <form onSubmit={handleSubmit} className="space-y-4">
                    <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">Numéro de téléphone *</label>
                        <input value={form.phone_number} onChange={(e) => update('phone_number', e.target.value)}
                            placeholder="+41225391400" className="input-field" />
                    </div>
                    <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">Description</label>
                        <input value={form.description} onChange={(e) => update('description', e.target.value)}
                            placeholder="Support client" className="input-field" />
                    </div>
                    <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">Compte Twilio *</label>
                        <select value={form.twilio_account_id} onChange={(e) => update('twilio_account_id', e.target.value)} className="input-field">
                            {twilioAccounts.map(a => <option key={a.id} value={a.id}>{a.name}</option>)}
                        </select>
                    </div>
                    <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">Type de ligne</label>
                        <select value={form.type} onChange={(e) => update('type', e.target.value)} className="input-field">
                            {Object.entries(LINE_TYPES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
                        </select>
                    </div>

                    {form.type === 'forward' && (
                        <div>
                            <label className="block text-sm font-medium text-gray-700 mb-1">Numéro de redirection</label>
                            <input value={form.forward_number} onChange={(e) => update('forward_number', e.target.value)}
                                placeholder="+33987654321" className="input-field" />
                        </div>
                    )}

                    {form.type === 'voice_ai' && (
                        <>
                            <div>
                                <label className="block text-sm font-medium text-gray-700 mb-1">Instructions IA</label>
                                <textarea value={form.voice_instructions} onChange={(e) => update('voice_instructions', e.target.value)}
                                    placeholder="Tu es l'assistant de..." className="input-field" rows="3" style={{ resize: 'vertical' }} />
                            </div>
                            <div>
                                <label className="block text-sm font-medium text-gray-700 mb-1">Message d'accueil</label>
                                <input value={form.voice_welcome_message} onChange={(e) => update('voice_welcome_message', e.target.value)}
                                    placeholder="Bonjour, comment puis-je vous aider ?" className="input-field" />
                            </div>
                            <div>
                                <label className="block text-sm font-medium text-gray-700 mb-1">Voix</label>
                                <select value={form.voice} onChange={(e) => update('voice', e.target.value)} className="input-field">
                                    {VOICES.map(v => <option key={v} value={v}>{v}</option>)}
                                </select>
                            </div>
                        </>
                    )}

                    <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">Numéro notification SMS</label>
                        <input value={form.notification_number} onChange={(e) => update('notification_number', e.target.value)}
                            placeholder="+33612345678" className="input-field" />
                    </div>

                    <div className="flex items-center justify-between">
                        <span className="text-sm font-medium text-gray-700">Enregistrer les appels</span>
                        <div className={`toggle ${form.record_calls ? 'active' : ''}`}
                            onClick={() => update('record_calls', form.record_calls ? 0 : 1)}></div>
                    </div>

                    <button type="submit" disabled={saving} className="btn-primary w-full">
                        {saving ? 'Création...' : 'Créer la ligne'}
                    </button>
                </form>
            </div>
        </div>
    );
};

// ==================== LINE DETAIL ====================
const LineDetailView = () => {
    const { selectedLine, navigate, loadLines, loadCalls, notify } = useApp();
    const [line, setLine] = useState(selectedLine);
    const [editing, setEditing] = useState(false);
    const [form, setForm] = useState({});
    const [saving, setSaving] = useState(false);
    const [lineCalls, setLineCalls] = useState([]);

    useEffect(() => {
        if (!selectedLine) { navigate('lines'); return; }
        setLine(selectedLine);
        setForm({ ...selectedLine });
        loadLineCalls();
    }, [selectedLine]);

    const loadLineCalls = async () => {
        if (!selectedLine) return;
        try {
            const data = await api.get(`/my/calls?line_id=${selectedLine.id}&limit=10`);
            setLineCalls(data.calls || []);
        } catch (e) { console.error(e); }
    };

    const update = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

    const handleSave = async () => {
        setSaving(true);
        try {
            const data = await api.put(`/my/lines/${line.id}`, form);
            if (data.success) { notify.success('Ligne mise à jour'); loadLines(); setEditing(false); }
            else notify.error(data.error || 'Erreur');
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setSaving(false); }
    };

    const handleToggle = async () => {
        try {
            await api.post('/api/lines/toggle', { id: line.id, active: !line.is_active });
            setLine(prev => ({ ...prev, is_active: prev.is_active ? 0 : 1 }));
            loadLines();
            notify.success(line.is_active ? 'Ligne désactivée' : 'Ligne activée');
        } catch (e) { notify.error('Erreur'); }
    };

    if (!line) return null;

    return (
        <div className="p-4 pb-24 space-y-4 fade-in-up">
            <div className="flex items-center gap-3">
                <button onClick={() => navigate('lines')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <div className="flex-1 min-w-0">
                    <h1 className="text-xl font-extrabold truncate">{formatPhone(line.phone_number)}</h1>
                    <p className="text-sm text-gray-500">{line.description || 'Sans description'}</p>
                </div>
                <button onClick={() => setEditing(!editing)} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.Edit className="w-5 h-5 text-vocal" />
                </button>
            </div>

            <div className="card p-4">
                <div className="flex items-center justify-between mb-3">
                    <span className={`type-badge ${LINE_TYPES[line.type]?.color}`}><LineTypeIcon type={line.type} className="w-3.5 h-3.5" /> {LINE_TYPES[line.type]?.label}</span>
                    <div className="flex items-center gap-2">
                        <span className="text-sm text-gray-500">{line.is_active ? 'Active' : 'Inactive'}</span>
                        <div className={`toggle ${line.is_active ? 'active' : ''}`} onClick={handleToggle}></div>
                    </div>
                </div>
                <div className="grid grid-cols-2 gap-3 mt-3">
                    <div><span className="text-xs text-gray-400">Appels</span><div className="font-bold">{line.total_calls || 0}</div></div>
                    <div><span className="text-xs text-gray-400">Durée totale</span><div className="font-bold">{formatDuration(line.total_duration || 0)}</div></div>
                    <div><span className="text-xs text-gray-400">Statut</span>
                        <span className={`badge ${STATUS_MAP[line.line_status]?.class}`}>{STATUS_MAP[line.line_status]?.label}</span></div>
                    <div><span className="text-xs text-gray-400">Créée</span><div className="text-sm font-medium">{formatDate(line.created_at)}</div></div>
                </div>
            </div>

            {editing && (
                <div className="card p-4 space-y-3">
                    <h3 className="font-bold text-gray-900">Modifier</h3>
                    <div>
                        <label className="block text-xs font-medium text-gray-500 mb-1">Description</label>
                        <input value={form.description || ''} onChange={(e) => update('description', e.target.value)} className="input-field" />
                    </div>
                    <div>
                        <label className="block text-xs font-medium text-gray-500 mb-1">Type</label>
                        <select value={form.type} onChange={(e) => update('type', e.target.value)} className="input-field">
                            {Object.entries(LINE_TYPES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
                        </select>
                    </div>
                    {(form.type === 'forward') && (
                        <div>
                            <label className="block text-xs font-medium text-gray-500 mb-1">Numéro de redirection</label>
                            <input value={form.forward_number || ''} onChange={(e) => update('forward_number', e.target.value)} className="input-field" />
                        </div>
                    )}
                    {(form.type === 'voice_ai') && (
                        <>
                            <div>
                                <label className="block text-xs font-medium text-gray-500 mb-1">Instructions IA</label>
                                <textarea value={form.voice_instructions || ''} onChange={(e) => update('voice_instructions', e.target.value)}
                                    className="input-field" rows="3" style={{ resize: 'vertical' }} />
                            </div>
                            <div>
                                <label className="block text-xs font-medium text-gray-500 mb-1">Voix</label>
                                <select value={form.voice || 'alloy'} onChange={(e) => update('voice', e.target.value)} className="input-field">
                                    {VOICES.map(v => <option key={v} value={v}>{v}</option>)}
                                </select>
                            </div>
                        </>
                    )}
                    <div>
                        <label className="block text-xs font-medium text-gray-500 mb-1">Notification SMS</label>
                        <input value={form.notification_number || ''} onChange={(e) => update('notification_number', e.target.value)} className="input-field" />
                    </div>
                    <div className="flex items-center justify-between">
                        <span className="text-sm font-medium">Enregistrer les appels</span>
                        <div className={`toggle ${form.record_calls ? 'active' : ''}`}
                            onClick={() => update('record_calls', form.record_calls ? 0 : 1)}></div>
                    </div>
                    <div className="flex gap-2">
                        <button onClick={handleSave} disabled={saving} className="btn-primary flex-1 text-sm py-2">
                            {saving ? 'Sauvegarde...' : 'Sauvegarder'}
                        </button>
                        <button onClick={() => setEditing(false)} className="btn-secondary flex-1 text-sm py-2">Annuler</button>
                    </div>
                </div>
            )}

            <div className="card p-4">
                <h3 className="font-bold text-gray-900 mb-3">Derniers appels</h3>
                {lineCalls.length === 0 ? (
                    <p className="text-sm text-gray-400 text-center py-4">Aucun appel</p>
                ) : (
                    <div className="space-y-2">
                        {lineCalls.map(call => (
                            <div key={call.id} className="flex items-center justify-between py-2 border-b border-gray-100 last:border-0">
                                <div>
                                    <div className="text-sm font-medium">{formatPhone(call.from_number)}</div>
                                    <div className="text-xs text-gray-400">{formatDateTime(call.created_at)}</div>
                                </div>
                                <div className="text-right">
                                    <span className={`badge text-xs ${CALL_STATUS[call.status]?.class || 'badge-gray'}`}>
                                        {CALL_STATUS[call.status]?.label || call.status}
                                    </span>
                                    <div className="text-xs text-gray-400 mt-1">{formatDuration(call.duration)}</div>
                                </div>
                            </div>
                        ))}
                    </div>
                )}
            </div>
        </div>
    );
};

// ==================== CALLS ====================
const CallsView = () => {
    const { calls, loading, loadCalls, lines, filteredLineId } = useApp();
    const [filterLine, setFilterLine] = useState('');
    const [page, setPage] = useState(1);

    useEffect(() => {
        if (filteredLineId) {
            setFilterLine(String(filteredLineId));
            loadCalls(1, filteredLineId);
        } else {
            setFilterLine('');
            loadCalls(1);
        }
    }, [filteredLineId]);

    const handleFilter = (lineId) => {
        setFilterLine(lineId);
        setPage(1);
        loadCalls(1, lineId || null);
    };

    const handlePage = (p) => {
        setPage(p);
        loadCalls(p, filterLine || null);
    };

    return (
        <div className="p-4 pb-24 space-y-4">
            <select value={filterLine} onChange={(e) => handleFilter(e.target.value)} className="input-field">
                <option value="">Toutes les lignes</option>
                {lines.map(l => <option key={l.id} value={l.id}>{formatPhone(l.phone_number)} - {l.description || l.label || ''}</option>)}
            </select>

            {loading && calls.calls?.length === 0 ? (
                <div className="flex justify-center py-12"><div className="spinner"></div></div>
            ) : (calls.calls || []).length === 0 ? (
                <div className="empty-state">
                    <Icons.PhoneIncoming className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucun appel</p>
                </div>
            ) : (
                <>
                    <div className="space-y-2">
                        {(calls.calls || []).map((call, i) => (
                            <div key={call.id} className="card p-3 stagger-item" style={{ animationDelay: `${i * 0.03}s` }}>
                                <div className="flex items-center justify-between">
                                    <div className="flex-1 min-w-0">
                                        <div className="flex items-center gap-2">
                                            <span className="font-bold text-sm">{formatPhone(call.from_number)}</span>
                                            <span className={`badge text-xs ${CALL_STATUS[call.status]?.class || 'badge-gray'}`}>
                                                {CALL_STATUS[call.status]?.label || call.status}
                                            </span>
                                        </div>
                                        <div className="text-xs text-gray-400 mt-1 flex items-center gap-1">
                                            <Icons.ArrowUpRight className="w-3 h-3" /> {formatPhone(call.to_number)} {call.description && `(${call.description})`}
                                        </div>
                                    </div>
                                    <div className="text-right flex-shrink-0 ml-3">
                                        <div className="text-sm font-medium">{formatDuration(call.duration)}</div>
                                        <div className="text-xs text-gray-400">{formatDateTime(call.created_at)}</div>
                                        {call.billed_price > 0 && <div className="text-xs text-vocal font-medium">{formatCurrency(call.billed_price)}</div>}
                                    </div>
                                </div>
                                {call.recording_url && (
                                    <div className="mt-2 flex items-center gap-2">
                                        <Icons.Volume className="w-4 h-4 text-vocal" />
                                        <a href={call.recording_url} target="_blank" className="text-xs text-vocal font-medium">Écouter l'enregistrement</a>
                                    </div>
                                )}
                            </div>
                        ))}
                    </div>

                    {calls.total_pages > 1 && (
                        <div className="flex items-center justify-center gap-2">
                            <button onClick={() => handlePage(page - 1)} disabled={page <= 1}
                                className="btn-secondary text-sm py-1.5 px-3 flex items-center gap-1"><Icons.ChevronLeft className="w-3 h-3" /> Préc.</button>
                            <span className="text-sm text-gray-500">{page} / {calls.total_pages}</span>
                            <button onClick={() => handlePage(page + 1)} disabled={page >= calls.total_pages}
                                className="btn-secondary text-sm py-1.5 px-3 flex items-center gap-1">Suiv. <Icons.ChevronRight className="w-3 h-3" /></button>
                        </div>
                    )}
                </>
            )}
        </div>
    );
};

// ==================== SETTINGS ====================
const SettingsView = () => {
    const { user, logout, navigate, twilioAccounts, loadTwilioAccounts, blacklist, loadBlacklist, notify } = useApp();
    const [subView, setSubView] = useState('main');
    const [showAddAccount, setShowAddAccount] = useState(false);
    const [showAddBlacklist, setShowAddBlacklist] = useState(false);

    if (subView === 'billing') return <BillingView onBack={() => setSubView('main')} />;

    useEffect(() => {
        if (subView === 'blacklist') loadBlacklist();
    }, [subView]);

    if (subView === 'twilio') return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center gap-3">
                <button onClick={() => setSubView('main')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Comptes Twilio</h1>
                <button onClick={() => setShowAddAccount(true)} className="btn-primary text-sm py-2 px-3">
                    <Icons.Plus className="w-4 h-4" />
                </button>
            </div>
            {twilioAccounts.length === 0 ? (
                <div className="empty-state"><p>Aucun compte Twilio</p></div>
            ) : (
                <div className="space-y-2">
                    {twilioAccounts.map(a => (
                        <div key={a.id} className="card p-4">
                            <div className="flex items-center justify-between">
                                <div>
                                    <div className="font-bold">{a.name}</div>
                                    <div className="text-xs text-gray-400 mt-1">{a.account_sid}</div>
                                    <div className="text-xs text-gray-500 mt-1">{a.total_lines} lignes · {a.total_calls} appels</div>
                                </div>
                                <div className={`w-3 h-3 rounded-full ${a.is_active ? 'bg-emerald-400' : 'bg-gray-300'}`}></div>
                            </div>
                        </div>
                    ))}
                </div>
            )}
            {showAddAccount && <AddTwilioModal onClose={() => { setShowAddAccount(false); loadTwilioAccounts(); }} />}
        </div>
    );

    if (subView === 'blacklist') return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center gap-3">
                <button onClick={() => setSubView('main')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Blacklist</h1>
                <button onClick={() => setShowAddBlacklist(true)} className="btn-primary text-sm py-2 px-3">
                    <Icons.Plus className="w-4 h-4" />
                </button>
            </div>
            {blacklist.length === 0 ? (
                <div className="empty-state"><p>Aucun numéro bloqué</p></div>
            ) : (
                <div className="space-y-2">
                    {blacklist.map(b => (
                        <div key={b.id} className="card p-4">
                            <div className="flex items-center justify-between">
                                <div>
                                    <div className="font-bold text-sm">{b.phone_number}</div>
                                    <div className="text-xs text-gray-400">{b.reason || 'Aucune raison'} · {b.type}</div>
                                </div>
                                <button onClick={async () => {
                                    await api.del(`/api/blacklist/${b.id}`);
                                    notify.success('Numéro débloqué');
                                    loadBlacklist();
                                }} className="text-red-400 hover:text-red-600">
                                    <Icons.Trash className="w-5 h-5" />
                                </button>
                            </div>
                        </div>
                    ))}
                </div>
            )}
            {showAddBlacklist && <AddBlacklistModal onClose={() => { setShowAddBlacklist(false); loadBlacklist(); }} />}
        </div>
    );

    return (
        <div className="p-4 pb-24 space-y-4">
            {user && (
                <div className="card p-4 flex items-center gap-4">
                    <div className="w-12 h-12 rounded-full flex items-center justify-center text-white font-bold text-lg" style={{ background: 'var(--gradient)' }}>
                        {(user.email || 'U')[0].toUpperCase()}
                    </div>
                    <div>
                        <div className="font-bold">{user.name || user.email}</div>
                        <div className="text-sm text-gray-500">{user.email}</div>
                    </div>
                </div>
            )}

            <div className="space-y-1">
                <button onClick={() => setSubView('billing')} className="w-full flex items-center justify-between p-4 bg-white rounded-xl hover:bg-gray-50 transition-all">
                    <div className="flex items-center gap-3">
                        <div className="w-10 h-10 rounded-xl bg-emerald-100 flex items-center justify-center"><Icons.Wallet className="w-5 h-5 text-emerald-600" /></div>
                        <div className="text-left"><div className="font-medium">Facturation & crédits</div><div className="text-xs text-gray-400">Solde, recharges, transactions</div></div>
                    </div>
                    <Icons.ChevronRight className="w-5 h-5 text-gray-400" />
                </button>
                <button onClick={() => navigate('voice')} className="w-full flex items-center justify-between p-4 bg-white rounded-xl hover:bg-gray-50 transition-all">
                    <div className="flex items-center gap-3">
                        <div className="w-10 h-10 rounded-xl bg-blue-100 flex items-center justify-center"><Icons.Headphones className="w-5 h-5 text-blue-600" /></div>
                        <div className="text-left"><div className="font-medium">Voice Lab</div><div className="text-xs text-gray-400">État du SDK & logs</div></div>
                    </div>
                    <Icons.ChevronRight className="w-5 h-5 text-gray-400" />
                </button>
            </div>

            <button onClick={logout} className="w-full flex items-center justify-center gap-2 p-4 bg-white rounded-xl text-red-500 font-medium hover:bg-red-50 transition-all mt-4">
                <Icons.LogOut className="w-5 h-5" /> Déconnexion
            </button>

            <div className="text-center mt-8 text-xs" style={{ color: 'var(--ios-label-tertiary)' }}>Vocal</div>
        </div>
    );
};

// ==================== ADD TWILIO MODAL ====================
const AddTwilioModal = ({ onClose }) => {
    const { notify } = useApp();
    const [form, setForm] = useState({ name: '', account_sid: '', auth_token: '', api_key_sid: '', api_key_secret: '' });
    const [saving, setSaving] = useState(false);
    const update = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

    const handleSubmit = async (e) => {
        e.preventDefault();
        if (!form.name || !form.account_sid) { notify.error('Nom et Account SID requis'); return; }
        setSaving(true);
        try {
            const data = await api.post('/api/twilio-accounts', form);
            if (data.success) { notify.success('Compte ajouté'); onClose(); }
            else notify.error(data.error || 'Erreur');
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setSaving(false); }
    };

    return (
        <div className="modal" onClick={onClose}>
            <div className="modal-content" onClick={(e) => e.stopPropagation()}>
                <div className="flex items-center justify-between mb-4">
                    <h2 className="text-lg font-bold">Ajouter un compte Twilio</h2>
                    <button onClick={onClose}><Icons.X className="w-6 h-6 text-gray-400" /></button>
                </div>
                <form onSubmit={handleSubmit} className="space-y-3">
                    <input value={form.name} onChange={(e) => update('name', e.target.value)} placeholder="Nom du compte *" className="input-field" />
                    <input value={form.account_sid} onChange={(e) => update('account_sid', e.target.value)} placeholder="Account SID (AC...) *" className="input-field" />
                    <input value={form.auth_token} onChange={(e) => update('auth_token', e.target.value)} placeholder="Auth Token" className="input-field" type="password" />
                    <input value={form.api_key_sid} onChange={(e) => update('api_key_sid', e.target.value)} placeholder="API Key SID (SK...)" className="input-field" />
                    <input value={form.api_key_secret} onChange={(e) => update('api_key_secret', e.target.value)} placeholder="API Key Secret" className="input-field" type="password" />
                    <button type="submit" disabled={saving} className="btn-primary w-full">{saving ? 'Ajout...' : 'Ajouter'}</button>
                </form>
            </div>
        </div>
    );
};

// ==================== ADD BLACKLIST MODAL ====================
const AddBlacklistModal = ({ onClose }) => {
    const { notify } = useApp();
    const [form, setForm] = useState({ phone_number: '', type: 'global', reason: '' });
    const [saving, setSaving] = useState(false);
    const update = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

    const handleSubmit = async (e) => {
        e.preventDefault();
        if (!form.phone_number) { notify.error('Numéro requis'); return; }
        setSaving(true);
        try {
            const data = await api.post('/api/blacklist', form);
            if (data.success) { notify.success('Numéro bloqué'); onClose(); }
            else notify.error(data.error || 'Erreur');
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setSaving(false); }
    };

    return (
        <div className="modal" onClick={onClose}>
            <div className="modal-content" onClick={(e) => e.stopPropagation()}>
                <div className="flex items-center justify-between mb-4">
                    <h2 className="text-lg font-bold">Bloquer un numéro</h2>
                    <button onClick={onClose}><Icons.X className="w-6 h-6 text-gray-400" /></button>
                </div>
                <form onSubmit={handleSubmit} className="space-y-3">
                    <input value={form.phone_number} onChange={(e) => update('phone_number', e.target.value)} placeholder="+33612345678 *" className="input-field" />
                    <select value={form.type} onChange={(e) => update('type', e.target.value)} className="input-field">
                        <option value="global">Global (toutes les lignes)</option>
                        <option value="user">Business spécifique</option>
                    </select>
                    <input value={form.reason} onChange={(e) => update('reason', e.target.value)} placeholder="Raison (optionnel)" className="input-field" />
                    <button type="submit" disabled={saving} className="btn-primary w-full">{saving ? 'Blocage...' : 'Bloquer'}</button>
                </form>
            </div>
        </div>
    );
};

// ==================== MARKETPLACE ====================
const MarketplaceView = () => {
    const { twilioAccounts, loadLines, navigate, notify } = useApp();
    const [numbers, setNumbers] = useState([]);
    const [loading, setLoading] = useState(false);
    const [accountId, setAccountId] = useState(twilioAccounts[0]?.id || '');
    const [areaCode, setAreaCode] = useState('');
    const [searchType, setSearchType] = useState('Local');
    const [purchasing, setPurchasing] = useState(null);

    const AREA_CODES = [
        { code: '', label: 'Tous' },
        { code: '21', label: '021 - Lausanne' },
        { code: '22', label: '022 - Genève' },
        { code: '24', label: '024 - Yverdon' },
        { code: '26', label: '026 - Fribourg' },
        { code: '27', label: '027 - Sion' },
        { code: '31', label: '031 - Berne' },
        { code: '32', label: '032 - Bienne' },
        { code: '41', label: '041 - Lucerne' },
        { code: '43', label: '043 - Zürich' },
        { code: '44', label: '044 - Zürich' },
        { code: '61', label: '061 - Bâle' },
        { code: '71', label: '071 - St-Gall' },
        { code: '91', label: '091 - Lugano' },
    ];

    const searchNumbers = async () => {
        if (!accountId) { notify.error('Sélectionnez un compte Twilio'); return; }
        setLoading(true);
        try {
            const params = `account_id=${accountId}&type=${searchType}&limit=20${areaCode ? `&area_code=${areaCode}` : ''}`;
            const data = await api.get(`/api/twilio/available-numbers?${params}`);
            setNumbers(data.numbers || []);
            if ((data.numbers || []).length === 0) notify.info('Aucun numéro trouvé');
        } catch (e) { notify.error('Erreur de recherche'); }
        finally { setLoading(false); }
    };

    const purchaseNumber = async (phoneNumber, friendlyName, locality) => {
        if (!confirm(`Acheter le numéro ${friendlyName || phoneNumber} ?`)) return;
        setPurchasing(phoneNumber);
        try {
            const data = await api.post('/api/twilio/purchase-number', {
                account_id: parseInt(accountId),
                phone_number: phoneNumber,
                label: locality ? `Ligne ${locality}` : 'Nouvelle ligne',
                description: friendlyName || phoneNumber,
                line_type: 'forward',
                line_status: 'available',
                monthly_price: 7,
                currency: 'CHF',
            });
            if (data.success) {
                notify.success(`Numéro ${phoneNumber} acheté !`);
                setNumbers(prev => prev.filter(n => n.phone_number !== phoneNumber));
                loadLines();
            } else {
                notify.error(data.error || 'Erreur d\'achat');
            }
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setPurchasing(null); }
    };

    return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center gap-3">
                <button onClick={() => navigate('settings')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Marketplace</h1>
            </div>

            <div className="card p-4 space-y-3">
                <div>
                    <label className="block text-xs font-medium text-gray-500 mb-1">Compte Twilio</label>
                    <select value={accountId} onChange={(e) => setAccountId(e.target.value)} className="input-field">
                        {twilioAccounts.map(a => <option key={a.id} value={a.id}>{a.name}</option>)}
                    </select>
                </div>
                <div className="grid grid-cols-2 gap-3">
                    <div>
                        <label className="block text-xs font-medium text-gray-500 mb-1">Indicatif</label>
                        <select value={areaCode} onChange={(e) => setAreaCode(e.target.value)} className="input-field">
                            {AREA_CODES.map(a => <option key={a.code} value={a.code}>{a.label}</option>)}
                        </select>
                    </div>
                    <div>
                        <label className="block text-xs font-medium text-gray-500 mb-1">Type</label>
                        <select value={searchType} onChange={(e) => setSearchType(e.target.value)} className="input-field">
                            <option value="Local">Local</option>
                            <option value="Mobile">Mobile</option>
                            <option value="TollFree">Gratuit</option>
                        </select>
                    </div>
                </div>
                <button onClick={searchNumbers} disabled={loading} className="btn-primary w-full flex items-center justify-center gap-2">
                    {loading ? <><div className="spinner" style={{ width: '1rem', height: '1rem', borderWidth: '2px' }}></div> Recherche...</> : <><Icons.Search className="w-4 h-4" /> Rechercher des numéros</>}
                </button>
            </div>

            {numbers.length > 0 && (
                <div className="space-y-2">
                    <p className="text-sm text-gray-500 font-medium">{numbers.length} numéros disponibles</p>
                    {numbers.map((num, i) => (
                        <div key={num.phone_number} className="card p-3 flex items-center justify-between stagger-item" style={{ animationDelay: `${i * 0.03}s` }}>
                            <div>
                                <div className="font-bold text-sm">{num.friendly_name}</div>
                                <div className="text-xs text-gray-400">{num.locality}{num.region ? ` (${num.region})` : ''}</div>
                                <div className="flex items-center gap-2 mt-1">
                                    {num.capabilities?.voice && <span className="badge badge-success text-xs">Voix</span>}
                                    {num.capabilities?.SMS && <span className="badge badge-info text-xs">SMS</span>}
                                </div>
                            </div>
                            <button onClick={() => purchaseNumber(num.phone_number, num.friendly_name, num.locality)}
                                disabled={purchasing === num.phone_number}
                                className="btn-primary text-xs py-1.5 px-3">
                                {purchasing === num.phone_number ? '...' : 'Acheter'}
                            </button>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== TTS / AUDIO FILES ====================
const AudioView = () => {
    const { navigate, notify } = useApp();
    const [audioFiles, setAudioFiles] = useState([]);
    const [loading, setLoading] = useState(true);
    const [showGenerate, setShowGenerate] = useState(false);

    const loadAudio = useCallback(async () => {
        setLoading(true);
        try {
            const data = await api.get('/api/audio-files');
            setAudioFiles(Array.isArray(data) ? data : []);
        } catch (e) { console.error(e); }
        finally { setLoading(false); }
    }, []);

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

    const deleteAudio = async (id) => {
        if (!confirm('Supprimer ce fichier audio ?')) return;
        try {
            await api.del(`/api/audio-files/${id}`);
            notify.success('Audio supprimé');
            loadAudio();
        } catch (e) { notify.error('Erreur'); }
    };

    return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center gap-3">
                <button onClick={() => navigate('settings')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Fichiers audio</h1>
                <button onClick={() => setShowGenerate(true)} className="btn-primary text-sm py-2 px-3 flex items-center gap-1">
                    <Icons.Volume className="w-4 h-4" /> Générer
                </button>
            </div>

            {loading ? (
                <div className="flex justify-center py-12"><div className="spinner"></div></div>
            ) : audioFiles.length === 0 ? (
                <div className="empty-state">
                    <Icons.Volume className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucun fichier audio</p>
                    <p className="text-sm mt-1">Générez des messages vocaux avec l'IA</p>
                </div>
            ) : (
                <div className="space-y-2">
                    {audioFiles.map((af, i) => (
                        <div key={af.id} className="card p-3 stagger-item" style={{ animationDelay: `${i * 0.05}s` }}>
                            <div className="flex items-center justify-between">
                                <div className="flex-1 min-w-0">
                                    <div className="font-bold text-sm truncate">{af.name || 'Sans nom'}</div>
                                    <div className="text-xs text-gray-400 mt-0.5">
                                        Voix: {af.voice || '-'} · {formatDate(af.created_at)}
                                    </div>
                                    {af.text_content && (
                                        <div className="text-xs text-gray-500 mt-1 line-clamp-2" style={{ display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
                                            « {af.text_content} »
                                        </div>
                                    )}
                                </div>
                                <div className="flex items-center gap-2 ml-3">
                                    {af.file_url && (
                                        <a href={af.file_url} target="_blank" className="p-2 rounded-lg hover:bg-indigo-50 text-vocal">
                                            <Icons.Volume className="w-5 h-5" />
                                        </a>
                                    )}
                                    <button onClick={() => deleteAudio(af.id)} className="p-2 rounded-lg hover:bg-red-50 text-red-400">
                                        <Icons.Trash className="w-5 h-5" />
                                    </button>
                                </div>
                            </div>
                        </div>
                    ))}
                </div>
            )}

            {showGenerate && <GenerateTTSModal onClose={() => { setShowGenerate(false); loadAudio(); }} />}
        </div>
    );
};

const GenerateTTSModal = ({ onClose }) => {
    const { notify } = useApp();
    const [form, setForm] = useState({ text: '', voice: 'nova', name: '' });
    const [generating, setGenerating] = useState(false);
    const [result, setResult] = useState(null);
    const update = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

    const handleGenerate = async (e) => {
        e.preventDefault();
        if (!form.text) { notify.error('Texte requis'); return; }
        setGenerating(true);
        try {
            const data = await api.post('/api/tts', form);
            if (data.success) {
                setResult(data);
                notify.success('Audio généré !');
            } else {
                notify.error(data.error || 'Erreur');
            }
        } catch (e) { notify.error('Erreur réseau'); }
        finally { setGenerating(false); }
    };

    return (
        <div className="modal" onClick={onClose}>
            <div className="modal-content" onClick={(e) => e.stopPropagation()}>
                <div className="flex items-center justify-between mb-4">
                    <h2 className="text-lg font-bold">Générer un audio TTS</h2>
                    <button onClick={onClose}><Icons.X className="w-6 h-6 text-gray-400" /></button>
                </div>
                {!result ? (
                    <form onSubmit={handleGenerate} className="space-y-3">
                        <div>
                            <label className="block text-sm font-medium text-gray-700 mb-1">Nom du fichier</label>
                            <input value={form.name} onChange={(e) => update('name', e.target.value)}
                                placeholder="ex: Accueil principal" className="input-field" />
                        </div>
                        <div>
                            <label className="block text-sm font-medium text-gray-700 mb-1">Texte à synthétiser *</label>
                            <textarea value={form.text} onChange={(e) => update('text', e.target.value)}
                                placeholder="Bienvenue chez notre entreprise. Comment puis-je vous aider ?"
                                className="input-field" rows="4" style={{ resize: 'vertical' }} />
                        </div>
                        <div>
                            <label className="block text-sm font-medium text-gray-700 mb-1">Voix</label>
                            <div className="grid grid-cols-3 gap-2">
                                {VOICES.map(v => (
                                    <button key={v} type="button" onClick={() => update('voice', v)}
                                        className={`py-2 px-3 rounded-lg text-sm font-medium border-2 transition-all ${form.voice === v ? 'border-vocal bg-indigo-50 text-vocal' : 'border-gray-200 text-gray-600 hover:border-gray-300'}`}>
                                        {v}
                                    </button>
                                ))}
                            </div>
                        </div>
                        <button type="submit" disabled={generating} className="btn-primary w-full flex items-center justify-center gap-2">
                            {generating ? <><div className="spinner" style={{ width: '1rem', height: '1rem', borderWidth: '2px' }}></div> Génération...</> : <><Icons.Volume className="w-4 h-4" /> Générer</>}
                        </button>
                    </form>
                ) : (
                    <div className="space-y-4 text-center">
                        <div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center mx-auto">
                            <Icons.CheckCircle className="w-8 h-8 text-green-600" />
                        </div>
                        <p className="font-bold text-lg">Audio généré !</p>
                        {result.audio_url && (
                            <audio controls src={result.audio_url} className="w-full" />
                        )}
                        <button onClick={onClose} className="btn-primary w-full">Fermer</button>
                    </div>
                )}
            </div>
        </div>
    );
};

// ==================== ERRORS / NOTIFICATIONS ====================
const ErrorsView = () => {
    const { navigate, notify } = useApp();
    const [errors, setErrors] = useState([]);
    const [loading, setLoading] = useState(true);

    const loadErrors = useCallback(async () => {
        setLoading(true);
        try {
            const data = await api.get('/api/errors?limit=50');
            setErrors(Array.isArray(data) ? data : []);
        } catch (e) { console.error(e); }
        finally { setLoading(false); }
    }, []);

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

    const markAllRead = async () => {
        try {
            await api.post('/api/errors/mark-all-read');
            notify.success('Toutes les erreurs marquées comme lues');
            loadErrors();
        } catch (e) { notify.error('Erreur'); }
    };

    const markRead = async (id) => {
        try {
            await api.post(`/api/errors/${id}/read`);
            setErrors(prev => prev.map(e => e.id === id ? { ...e, is_readed: 1 } : e));
        } catch (e) { console.error(e); }
    };

    const unreadCount = errors.filter(e => !e.is_readed).length;

    return (
        <div className="p-4 pb-24 space-y-4">
            <div className="flex items-center gap-3">
                <button onClick={() => navigate('settings')} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Erreurs</h1>
                {unreadCount > 0 && (
                    <button onClick={markAllRead} className="text-sm text-vocal font-medium">
                        Tout marquer lu ({unreadCount})
                    </button>
                )}
            </div>

            {loading ? (
                <div className="flex justify-center py-12"><div className="spinner"></div></div>
            ) : errors.length === 0 ? (
                <div className="empty-state">
                    <Icons.Shield className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucune erreur</p>
                    <p className="text-sm mt-1">Tout fonctionne correctement</p>
                </div>
            ) : (
                <div className="space-y-2">
                    {errors.map((err, i) => (
                        <div key={err.id}
                            className={`card p-3 stagger-item ${!err.is_readed ? 'border-l-4 border-l-red-400' : ''}`}
                            style={{ animationDelay: `${i * 0.03}s` }}
                            onClick={() => !err.is_readed && markRead(err.id)}>
                            <div className="flex items-center justify-between">
                                <div className="flex-1 min-w-0">
                                    <div className="text-sm font-medium truncate">
                                        {(() => {
                                            try { const e = JSON.parse(err.error_result); return e.error || e.message || 'Erreur'; }
                                            catch(e) { return err.error_result || 'Erreur inconnue'; }
                                        })()}
                                    </div>
                                    <div className="text-xs text-gray-400 mt-0.5">
                                        {err.line_id ? `Ligne #${err.line_id}` : 'Système'} · {formatDateTime(err.created_at)}
                                    </div>
                                </div>
                                {!err.is_readed && <div className="w-2.5 h-2.5 rounded-full bg-red-400 flex-shrink-0 ml-2"></div>}
                            </div>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== DAILY STATS CHART ====================
const DailyChart = () => {
    const { filteredLineId } = useApp();
    const [dailyData, setDailyData] = useState([]);
    const canvasRef = useRef(null);

    useEffect(() => {
        const load = async () => {
            try {
                const from = new Date(Date.now() - 14 * 86400000).toISOString().split('T')[0];
                const to = new Date().toISOString().split('T')[0];
                let url = `/my/calls/daily-stats?from=${from}&to=${to}`;
                if (filteredLineId) url += `&line_id=${filteredLineId}`;
                const data = await api.get(url);
                setDailyData(data.daily || []);
            } catch (e) { console.error(e); }
        };
        load();
    }, [filteredLineId]);

    useEffect(() => {
        if (!canvasRef.current || dailyData.length === 0) return;
        const canvas = canvasRef.current;
        const ctx = canvas.getContext('2d');
        const dpr = window.devicePixelRatio || 1;
        const rect = canvas.getBoundingClientRect();
        canvas.width = rect.width * dpr;
        canvas.height = rect.height * dpr;
        ctx.scale(dpr, dpr);
        ctx.clearRect(0, 0, rect.width, rect.height);

        const maxCalls = Math.max(...dailyData.map(d => d.calls), 1);
        const padding = { top: 10, right: 10, bottom: 30, left: 10 };
        const w = rect.width - padding.left - padding.right;
        const h = rect.height - padding.top - padding.bottom;
        const barWidth = Math.min(w / dailyData.length - 4, 24);

        const gradient = ctx.createLinearGradient(0, padding.top, 0, padding.top + h);
        gradient.addColorStop(0, '#6366f1');
        gradient.addColorStop(1, '#818cf8');

        dailyData.forEach((d, i) => {
            const barH = (d.calls / maxCalls) * h;
            const x = padding.left + (w / dailyData.length) * i + (w / dailyData.length - barWidth) / 2;
            const y = padding.top + h - barH;

            ctx.fillStyle = gradient;
            ctx.beginPath();
            const r = 3;
            ctx.moveTo(x + r, y);
            ctx.lineTo(x + barWidth - r, y);
            ctx.quadraticCurveTo(x + barWidth, y, x + barWidth, y + r);
            ctx.lineTo(x + barWidth, y + barH);
            ctx.lineTo(x, y + barH);
            ctx.lineTo(x, y + r);
            ctx.quadraticCurveTo(x, y, x + r, y);
            ctx.fill();

            ctx.fillStyle = '#94a3b8';
            ctx.font = '9px Inter, sans-serif';
            ctx.textAlign = 'center';
            const dayLabel = d.day.slice(8);
            ctx.fillText(dayLabel, x + barWidth / 2, padding.top + h + 16);
        });
    }, [dailyData]);

    if (dailyData.length === 0) return null;

    return (
        <div className="card p-4">
            <h3 className="font-bold text-gray-900 mb-3">Appels (14 derniers jours)</h3>
            <canvas ref={canvasRef} style={{ width: '100%', height: '120px' }}></canvas>
        </div>
    );
};

// ==================== LINE SELECTOR ====================
const LineSelector = () => {
    const { lines, filteredLine, filteredLineId, setFilteredLineId } = useApp();
    const [open, setOpen] = useState(false);
    const selectorRef = useRef(null);

    useEffect(() => {
        if (!open) return;
        const handler = (e) => {
            if (selectorRef.current && !selectorRef.current.contains(e.target)) setOpen(false);
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [open]);

    const activeLines = lines.filter(l => l.is_active);

    return (
        <div ref={selectorRef} className="relative flex-1 min-w-0">
            <button onClick={() => setOpen(!open)}
                className="line-selector-btn">
                <div className="line-selector-icon">
                    <Icons.Phone className="w-3.5 h-3.5 text-white" />
                </div>
                <span className="truncate">{filteredLine ? (formatPhone(filteredLine.phone_number)) : 'Toutes les lignes'}</span>
                {filteredLine && (
                    <span className={`type-badge-sm ${LINE_TYPES[filteredLine.type]?.color || ''}`}>
                        <LineTypeIcon type={filteredLine.type} className="w-3 h-3" />
                    </span>
                )}
                <svg className={`w-4 h-4 flex-shrink-0 transition-transform ${open ? 'rotate-180' : ''}`} style={{ opacity: 0.7 }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7"/>
                </svg>
            </button>
            {open && (
                <div className="line-selector-dropdown">
                    <button onClick={() => { setFilteredLineId(null); setOpen(false); }}
                        className={`line-selector-item ${!filteredLineId ? 'active' : ''}`}>
                        <div className="line-selector-item-icon all">
                            <Icons.Phone className="w-3 h-3 text-white" />
                        </div>
                        <div className="flex-1 min-w-0">
                            <span className="font-medium">Toutes les lignes</span>
                            <span className="text-xs text-gray-400 ml-1">({activeLines.length})</span>
                        </div>
                    </button>
                    <div className="line-selector-divider"></div>
                    {lines.map(line => (
                        <button key={line.id} onClick={() => { setFilteredLineId(line.id); setOpen(false); }}
                            className={`line-selector-item ${filteredLineId === line.id ? 'active' : ''}`}>
                            <div className={`line-selector-item-icon ${line.is_active ? '' : 'inactive'}`}>
                                <Icons.Phone className="w-3 h-3 text-white" />
                            </div>
                            <div className="flex-1 min-w-0">
                                <div className="text-sm font-medium truncate">{formatPhone(line.phone_number)}</div>
                                <div className="text-xs text-gray-400 truncate">{line.description || line.label || LINE_TYPES[line.type]?.label}</div>
                            </div>
                            <div className="flex items-center gap-1.5 flex-shrink-0">
                                <span className={`type-badge-sm ${LINE_TYPES[line.type]?.color || ''}`}><LineTypeIcon type={line.type} className="w-3 h-3" /></span>
                                <div className={`w-2 h-2 rounded-full ${line.is_active ? 'bg-emerald-400' : 'bg-gray-300'}`}></div>
                            </div>
                        </button>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== TOP BAR ====================
const TOP_BAR_TITLES = {
    myline: 'Ma ligne',
    calls: 'Récents',
    dialer: 'Clavier',
    whatsapp: 'WhatsApp',
    settings: 'Réglages',
    marketplace: 'Marketplace',
    audio: 'Audio',
    errors: 'Erreurs',
    voice: 'Voice Lab',
    'line-detail': 'Ligne',
    lines: 'Lignes',
    dashboard: 'Accueil',
};

const TopBar = () => {
    return (
        <div className="top-bar safe-area-top">
            <div className="top-bar-inner">
                <LineSelector />
            </div>
        </div>
    );
};

// ==================== DIALER (Appeler) ====================
const DialerView = () => {
    const { lines, navigate, notify } = useApp();
    const { makeCall, voiceStatus } = useVoice();
    const [number, setNumber] = useState('');
    const [calling, setCalling] = useState(false);

    const append = (d) => setNumber(prev => (prev + d).slice(0, 20));
    const backspace = () => setNumber(prev => prev.slice(0, -1));

    const handleCall = async () => {
        if (!number || number.length < 3) return;
        const to = number.startsWith('+') ? number : (number.startsWith('00') ? '+' + number.slice(2) : '+' + number);
        setCalling(true);
        const r = await makeCall(to);
        setCalling(false);
        if (r?.error) notify.error(r.error);
        else { notify.success(`Appel vers ${to}`); setNumber(''); }
    };

    const keys = [
        ['1', ''], ['2', 'ABC'], ['3', 'DEF'],
        ['4', 'GHI'], ['5', 'JKL'], ['6', 'MNO'],
        ['7', 'PQRS'], ['8', 'TUV'], ['9', 'WXYZ'],
        ['*', ''], ['0', '+'], ['#', ''],
    ];

    const activeLine = lines.find(l => l.is_active);

    return (
        <div className="dialer-view fade-in-up">
            <div className="dialer-header">
                <button onClick={() => navigate('myline')} className="dialer-close">
                    <Icons.X className="w-6 h-6" />
                </button>
                <div className="dialer-line">
                    {activeLine ? (
                        <><span className="dialer-line-label">Appel depuis</span>
                          <span className="dialer-line-num">{activeLine.phone_number}</span></>
                    ) : (
                        <span className="dialer-line-label">Aucune ligne active</span>
                    )}
                </div>
                <div style={{ width: 40 }} />
            </div>

            <div className="dialer-screen">
                <div className="dialer-number">
                    {number || <span className="dialer-placeholder">Numéro</span>}
                </div>
                {number && (
                    <div className="dialer-hint">{number.startsWith('+') ? number : '+' + number.replace(/^00/, '')}</div>
                )}
            </div>

            <div className="dialer-keypad">
                {keys.map(([d, letters]) => (
                    <button key={d} onClick={() => append(d)} className="dialer-key">
                        <span className="dialer-key-digit">{d}</span>
                        {letters && <span className="dialer-key-letters">{letters}</span>}
                    </button>
                ))}
            </div>

            <div className="dialer-actions">
                <div style={{ width: 64 }} />
                <button onClick={handleCall} disabled={calling || !number}
                    className="dialer-call-btn">
                    {calling ? (
                        <div className="animate-spin w-7 h-7 border-3 border-white border-t-transparent rounded-full"></div>
                    ) : (
                        <Icons.Phone className="w-8 h-8" />
                    )}
                </button>
                <button onClick={backspace} disabled={!number} className="dialer-backspace">
                    <Icons.Backspace className="w-6 h-6" />
                </button>
            </div>

            {voiceStatus !== 'ready' && voiceStatus !== 'in-call' && (
                <p className="dialer-status">Voice: {VOICE_STATUS[voiceStatus]?.label || voiceStatus}</p>
            )}
        </div>
    );
};

// ==================== MA LIGNE ====================
const MyLineView = () => {
    const { lines, loadLines, navigate, setSelectedLine, notify, loadStats, stats } = useApp();
    const [refreshing, setRefreshing] = useState(false);

    useEffect(() => { loadLines(); loadStats(); }, []);

    const refresh = async () => {
        setRefreshing(true);
        await Promise.all([loadLines(), loadStats()]);
        setRefreshing(false);
        notify.success('Mis à jour');
    };

    const activeLines = lines.filter(l => l.is_active !== 0);
    const main = activeLines[0];

    return (
        <div className="p-4 pb-24 space-y-4 fade-in-up">
            {main && (
                <div className="myline-hero">
                    <div className="myline-hero-bg" />
                    <div className="myline-hero-content">
                        <div className="myline-hero-label">Ligne principale</div>
                        <div className="myline-hero-number">{main.phone_number}</div>
                        <div className="myline-hero-desc">{main.description || main.label || 'Aucune description'}</div>
                        <div className="myline-hero-stats">
                            <div>
                                <div className="myline-stat-val">{main.calls_today || 0}</div>
                                <div className="myline-stat-lbl">Appels aujourd'hui</div>
                            </div>
                            <div>
                                <div className="myline-stat-val">{stats?.calls_month || stats?.total_calls || 0}</div>
                                <div className="myline-stat-lbl">Ce mois</div>
                            </div>
                            <div>
                                <div className="myline-stat-val">{LINE_TYPES[main.type]?.label || main.type}</div>
                                <div className="myline-stat-lbl">Mode</div>
                            </div>
                        </div>
                    </div>
                </div>
            )}

            <div className="grid grid-cols-2 gap-3">
                <button onClick={() => navigate('dialer')} className="myline-action-card">
                    <div className="myline-action-icon" style={{ background: '#10b981' }}>
                        <Icons.Phone className="w-6 h-6 text-white" />
                    </div>
                    <div className="myline-action-label">Appeler</div>
                </button>
                <button onClick={() => navigate('calls')} className="myline-action-card">
                    <div className="myline-action-icon" style={{ background: 'var(--primary)' }}>
                        <Icons.PhoneIncoming className="w-6 h-6 text-white" />
                    </div>
                    <div className="myline-action-label">Historique</div>
                </button>
                {main && (
                    <>
                        <button onClick={() => { setSelectedLine(main); navigate('line-detail'); }} className="myline-action-card">
                            <div className="myline-action-icon" style={{ background: '#8b5cf6' }}>
                                <Icons.Settings className="w-6 h-6 text-white" />
                            </div>
                            <div className="myline-action-label">Configurer</div>
                        </button>
                        <button onClick={() => navigate('whatsapp')} className="myline-action-card">
                            <div className="myline-action-icon" style={{ background: '#25d366' }}>
                                <Icons.MessageCircle className="w-6 h-6 text-white" />
                            </div>
                            <div className="myline-action-label">WhatsApp</div>
                        </button>
                    </>
                )}
            </div>

            {lines.length > 1 && (
                <div className="card p-4">
                    <div className="font-bold text-gray-900 mb-3">Autres lignes ({lines.length - 1})</div>
                    <div className="space-y-2">
                        {lines.slice(1).map(l => (
                            <button key={l.id} onClick={() => { setSelectedLine(l); navigate('line-detail'); }}
                                className="w-full flex items-center justify-between p-2 rounded-lg hover:bg-gray-50">
                                <div className="flex items-center gap-3">
                                    <LineTypeIcon type={l.type} className="w-5 h-5 text-gray-500" />
                                    <div className="text-left">
                                        <div className="font-medium text-sm">{l.phone_number}</div>
                                        <div className="text-xs text-gray-400">{l.description || l.label || '-'}</div>
                                    </div>
                                </div>
                                <Icons.ChevronRight className="w-5 h-5 text-gray-300" />
                            </button>
                        ))}
                    </div>
                </div>
            )}

            {lines.length === 0 && (
                <div className="empty-state">
                    <Icons.Phone className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucune ligne sur ce compte</p>
                    <p className="text-xs mt-1">Contactez Vocal pour activer un numéro</p>
                </div>
            )}
        </div>
    );
};

// ==================== WHATSAPP ====================
const WhatsAppView = () => {
    const { notify } = useApp();
    const [conversations, setConversations] = useState([]);
    const [loading, setLoading] = useState(true);
    const [selected, setSelected] = useState(null);
    const [messages, setMessages] = useState([]);
    const [draft, setDraft] = useState('');
    const [sending, setSending] = useState(false);

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const r = await api.get('/my/whatsapp/conversations');
            setConversations(r.conversations || []);
        } catch (e) { notify.error('Erreur chargement'); }
        finally { setLoading(false); }
    }, [notify]);

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

    const openConv = async (conv) => {
        setSelected(conv);
        try {
            const r = await api.get(`/my/whatsapp/conversations/${conv.id}/messages`);
            setMessages(r.messages || []);
        } catch (e) { notify.error('Erreur'); }
    };

    const send = async () => {
        if (!draft.trim() || !selected) return;
        setSending(true);
        try {
            const r = await api.post(`/my/whatsapp/conversations/${selected.id}/reply`, { body: draft });
            if (r?.error) notify.error(r.error);
            else { setDraft(''); openConv(selected); }
        } catch (e) { notify.error('Erreur envoi'); }
        finally { setSending(false); }
    };

    if (selected) {
        return (
            <div className="wa-thread">
                <div className="wa-thread-head">
                    <button onClick={() => { setSelected(null); setMessages([]); }} className="p-2">
                        <Icons.ChevronLeft className="w-5 h-5" />
                    </button>
                    <div className="flex-1 min-w-0">
                        <div className="font-bold text-sm truncate">{selected.contact_name || selected.contact_number}</div>
                        <div className="text-xs text-gray-400">{selected.contact_number}</div>
                    </div>
                </div>
                <div className="wa-thread-bg">
                    {messages.length === 0 && <p className="text-center text-gray-400 text-sm mt-8">Aucun message</p>}
                    {messages.map(m => (
                        <div key={m.id} className={`wa-msg-row ${m.direction === 'outbound' ? 'out' : 'in'}`}>
                            <div className="wa-msg-bubble">
                                {m.body || (m.num_media > 0 ? 'Média' : '')}
                                <div className="wa-msg-time">{formatDateTime(m.created_at)}</div>
                            </div>
                        </div>
                    ))}
                </div>
                <div className="wa-compose">
                    <textarea rows={1} value={draft} onChange={e => setDraft(e.target.value)}
                        placeholder="Répondre..." />
                    <button onClick={send} disabled={sending || !draft.trim()} className="wa-send-btn">
                        {sending ? '...' : 'Envoyer'}
                    </button>
                </div>
            </div>
        );
    }

    return (
        <div className="p-4 pb-24 space-y-3 fade-in-up">
            {loading ? (
                <div className="flex justify-center py-12"><div className="spinner"></div></div>
            ) : conversations.length === 0 ? (
                <div className="empty-state">
                    <Icons.MessageCircle className="w-12 h-12 text-gray-300 mb-3" />
                    <p className="font-medium">Aucune conversation</p>
                    <p className="text-xs mt-1">Activez WhatsApp Business sur une ligne pour commencer</p>
                </div>
            ) : (
                <div className="space-y-1">
                    {conversations.map(c => (
                        <button key={c.id} onClick={() => openConv(c)} className="wa-conv-item">
                            <div className="wa-conv-avatar">
                                {(c.contact_name || c.contact_number || '?')[0].toUpperCase()}
                            </div>
                            <div className="flex-1 min-w-0 text-left">
                                <div className="flex justify-between gap-2">
                                    <div className="font-bold text-sm truncate">{c.contact_name || c.contact_number}</div>
                                    <div className="text-xs text-gray-400 flex-shrink-0">{formatDateTime(c.last_message_at)}</div>
                                </div>
                                <div className="flex justify-between gap-2 mt-1">
                                    <div className="text-xs text-gray-500 truncate flex-1">{c.last_message_preview || c.contact_number}</div>
                                    {c.unread_count > 0 && <span className="wa-unread">{c.unread_count}</span>}
                                </div>
                            </div>
                        </button>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== BILLING / CRÉDITS ====================
const BillingView = ({ onBack }) => {
    const { notify } = useApp();
    const [balance, setBalance] = useState(null);
    const [transactions, setTransactions] = useState([]);
    const [loading, setLoading] = useState(true);
    const [recharging, setRecharging] = useState(false);

    const load = useCallback(async () => {
        setLoading(true);
        try {
            const [b, t] = await Promise.all([
                api.get('/my/billing/balance'),
                api.get('/my/billing/transactions'),
            ]);
            if (b && !b.error) setBalance(b);
            if (t?.transactions) setTransactions(t.transactions);
        } catch (e) {}
        finally { setLoading(false); }
    }, []);

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

    const recharge = async (amount) => {
        setRecharging(true);
        try {
            const r = await api.post('/my/billing/recharge', { amount, currency: 'CHF' });
            if (r?.url) window.location.href = r.url;
            else if (r?.error) notify.error(r.error);
        } catch (e) { notify.error('Erreur'); }
        finally { setRecharging(false); }
    };

    return (
        <div className="p-4 pb-24 space-y-4 fade-in-up">
            <div className="flex items-center gap-3">
                <button onClick={onBack} className="p-2 rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-6 h-6" />
                </button>
                <h1 className="text-xl font-extrabold flex-1">Facturation & crédits</h1>
            </div>

            <div className="billing-balance">
                <div className="billing-balance-label">Solde disponible</div>
                <div className="billing-balance-val">
                    {loading ? '...' : formatCurrency(balance?.balance ?? 0)}
                </div>
                {balance?.threshold && (
                    <div className="billing-balance-hint">Recharge auto sous {formatCurrency(balance.threshold)}</div>
                )}
            </div>

            <div className="font-bold text-gray-700">Recharger</div>
            <div className="grid grid-cols-3 gap-2">
                {[10, 25, 50, 100, 200, 500].map(a => (
                    <button key={a} onClick={() => recharge(a)} disabled={recharging}
                        className="billing-recharge-btn">
                        {a} CHF
                    </button>
                ))}
            </div>

            <div className="font-bold text-gray-700 mt-4">Dernières transactions</div>
            {loading ? (
                <div className="flex justify-center py-6"><div className="spinner"></div></div>
            ) : transactions.length === 0 ? (
                <div className="empty-state text-sm">Aucune transaction</div>
            ) : (
                <div className="space-y-2">
                    {transactions.slice(0, 20).map(t => (
                        <div key={t.id} className="card p-3 flex justify-between items-center">
                            <div>
                                <div className="font-medium text-sm">{t.description || t.type}</div>
                                <div className="text-xs text-gray-400">{formatDateTime(t.created_at)}</div>
                            </div>
                            <div className={`font-bold text-sm ${t.amount > 0 ? 'text-emerald-600' : 'text-red-500'}`}>
                                {t.amount > 0 ? '+' : ''}{formatCurrency(t.amount)}
                            </div>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== BOTTOM NAV ====================
const BottomNav = () => {
    const { view, navigate, loadCalls } = useApp();

    const sideItems = [
        { id: 'myline', icon: Icons.Phone, label: 'Ma ligne' },
        { id: 'calls', icon: Icons.PhoneIncoming, label: 'Appels', action: () => loadCalls() },
    ];
    const sideItemsRight = [
        { id: 'whatsapp', icon: Icons.MessageCircle, label: 'WhatsApp' },
        { id: 'settings', icon: Icons.Settings, label: 'Réglages' },
    ];

    const activeViews = {
        myline: 'myline', dashboard: 'myline',
        lines: 'myline', 'line-detail': 'myline',
        calls: 'calls',
        whatsapp: 'whatsapp',
        settings: 'settings', marketplace: 'settings', audio: 'settings',
        errors: 'settings', voice: 'settings', billing: 'settings',
    };

    const isCallView = view === 'dialer';

    const renderItem = (item) => {
        const isActive = activeViews[view] === item.id;
        return (
            <button key={item.id} onClick={() => { navigate(item.id); item.action?.(); }}
                className={`nav-item-v3 ${isActive ? 'active' : ''}`}>
                <item.icon className="w-6 h-6" />
                <span className="nav-item-v3-label">{item.label}</span>
            </button>
        );
    };

    return (
        <div className="bottom-nav-v3 safe-area-bottom">
            <div className="bottom-nav-v3-inner">
                {sideItems.map(renderItem)}
                <button onClick={() => navigate('dialer')}
                    className={`nav-call-btn ${isCallView ? 'active' : ''}`}
                    aria-label="Appeler">
                    <Icons.Phone className="w-8 h-8" />
                </button>
                {sideItemsRight.map(renderItem)}
            </div>
        </div>
    );
};

// ==================== APP ROOT ====================
const VoiceAutoInit = () => {
    const { user } = useApp();
    const { initVoice, voiceStatus } = useVoice();
    useEffect(() => {
        if (user && voiceStatus === 'offline') {
            initVoice();
        }
    }, [user]);
    return null;
};

const AppInner = () => {
    const { user, view, ready } = useApp();

    if (!ready) return (
        <div className="h-full flex items-center justify-center">
            <div className="text-center">
                <h1 style={{ fontSize: '1.5rem', fontWeight: 700, color: 'var(--ios-label)', margin: '0 0 16px' }}>Vocal</h1>
                <div className="animate-spin w-6 h-6 border-2 border-primary border-t-transparent rounded-full mx-auto"></div>
            </div>
        </div>
    );

    if (!user) return <LoginScreen />;

    const isDialer = view === 'dialer';

    return (
        <VoiceProvider>
            <VoiceAutoInit />
            <div className="h-full flex flex-col">
                {!isDialer && <TopBar />}
                {!isDialer && <ActiveCallBar />}
                <div className={`flex-1 overflow-y-auto ${isDialer ? '' : 'top-bar-offset'}`}>
                    {view === 'myline' && <MyLineView />}
                    {view === 'dashboard' && <DashboardView />}
                    {view === 'lines' && <LinesView />}
                    {view === 'line-detail' && <LineDetailView />}
                    {view === 'voice' && <VoiceTestView />}
                    {view === 'calls' && <CallsView />}
                    {view === 'dialer' && <DialerView />}
                    {view === 'whatsapp' && <WhatsAppView />}
                    {view === 'settings' && <SettingsView />}
                    {view === 'marketplace' && <MarketplaceView />}
                    {view === 'audio' && <AudioView />}
                    {view === 'errors' && <ErrorsView />}
                </div>
                <IncomingCallOverlay />
                <BottomNav />
            </div>
        </VoiceProvider>
    );
};

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

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