// ============================================================================
// EasyTrack Client App - Main Application
// ============================================================================

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

// Configuration
const CONFIG = {
    API_URL: 'https://api.easytrack.ch',
    WS_URL: 'wss://track.easytrack.ch/ws',
    MAP_CENTER: [46.8182, 8.2275],
    MAP_ZOOM: 8,
    TILE_LAYERS: {
        google: { url: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google Maps', label: 'Google' },
        satellite: { url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google Satellite', label: 'Satellite' },
        hybrid: { url: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google Hybrid', label: 'Hybride' }
    },
    MAP_TINTS: {
        standard: { label: 'Couleur', filter: 'none', preview: 'linear-gradient(135deg, #4ade80, #3b82f6, #f59e0b)' },
        grayDark: { label: 'Gris foncé', filter: 'grayscale(100%) brightness(0.7) invert(1)', preview: 'linear-gradient(135deg, #374151, #1f2937)' }
    }
};

// Vehicle SVG map (same as admin)
const VEHICLE_SVG_MAP = {
    'Voiture': { src: 'assets/svg/voiture.svg', ratio: 18.81 / 9.14 },
    'Limousine': { src: 'assets/svg/limousine.svg', ratio: 32.52 / 9.8 },
    'Minibus': { src: 'assets/svg/minibus.svg', ratio: 20.4 / 9.8 },
    'Pickup': { src: 'assets/svg/pickup.svg', ratio: 20.4 / 9.8 },
    'SUV': { src: 'assets/svg/suv.svg', ratio: 19.56 / 9.94 },
    'Sport': { src: 'assets/svg/sportcar.svg', ratio: 17.21 / 9.14 },
    'Minibus Premium': { src: 'assets/svg/premium_minibus.svg', ratio: 20.4 / 9.8 },
    'Premium Limousine': { src: 'assets/svg/premium_vehicle.svg', ratio: 18.81 / 9.14 },
    'Chariot élévateur': { src: 'assets/svg/elevator.svg', ratio: 14.49 / 7.59 },
    'Construction': { src: 'assets/svg/construction.svg', ratio: 21.3 / 15.34 },
    'Camion': { src: 'assets/svg/truck.svg', ratio: 25.8 / 10.62 },
    'Ambulance': { src: 'assets/svg/ambulance.svg', ratio: 22.28 / 9.8 }
};

function getVehicleImg(vehicleType, w) {
    const info = VEHICLE_SVG_MAP[vehicleType] || VEHICLE_SVG_MAP['Voiture'];
    const h = Math.round(w * info.ratio);
    return `<img src="${info.src}" width="${w}" height="${h}" style="pointer-events:none;" />`;
}

function getVehicleSize(vehicleType, w) {
    const info = VEHICLE_SVG_MAP[vehicleType] || VEHICLE_SVG_MAP['Voiture'];
    return Math.round(w * info.ratio);
}

// Status helper using ignition + movement matrix
function getDeviceStatus(speed, lastSeenAt, isActive, ignition, movement) {
    if (isActive === false || isActive === 0) {
        return { status: 'disabled', label: 'Désactivé', colors: { main: '#6B7280', bg: '#F3F4F6', text: '#4B5563' } };
    }
    let diffMinutes = 999;
    if (lastSeenAt) {
        let dateStr = String(lastSeenAt).replace(' ', 'T');
        if (!dateStr.endsWith('Z') && !dateStr.includes('+')) dateStr += 'Z';
        const lastSeen = new Date(dateStr);
        diffMinutes = (new Date() - lastSeen) / 1000 / 60;
        if (diffMinutes < 0) diffMinutes = 0;
    }
    if (speed > 3) {
        return { status: 'moving', label: 'En route', colors: { main: '#22C55E', bg: '#DCFCE7', text: '#166534' } };
    }
    if (diffMinutes < 5) {
        return { status: 'stopped', label: 'À l\'arrêt', colors: { main: '#F97316', bg: '#FFF7ED', text: '#9A3412' } };
    }
    return { status: 'parked', label: 'Parqué', colors: { main: '#3B82F6', bg: '#EFF6FF', text: '#1E40AF' } };
}

function formatParkingDuration(timestamp) {
    if (!timestamp) return null;
    let dateStr = String(timestamp).replace(' ', 'T');
    if (!dateStr.endsWith('Z') && !dateStr.includes('+')) dateStr += 'Z';
    const lastSeen = new Date(dateStr);
    const diffMinutes = Math.floor((new Date() - lastSeen) / 60000);
    if (diffMinutes < 2) return null;
    if (diffMinutes < 60) return `Depuis ${diffMinutes} min.`;
    const hours = Math.floor(diffMinutes / 60);
    const mins = diffMinutes % 60;
    return `Depuis ${hours}h${mins > 0 ? String(mins).padStart(2, '0') : '00'}`;
}

function formatStopDuration(timestamp) {
    if (!timestamp) return null;
    let dateStr = String(timestamp).replace(' ', 'T');
    if (!dateStr.endsWith('Z') && !dateStr.includes('+')) dateStr += 'Z';
    const lastSeen = new Date(dateStr);
    const diffSec = Math.floor((new Date() - lastSeen) / 1000);
    if (diffSec < 0) return null;
    if (diffSec < 60) return `${diffSec} sec.`;
    const mins = Math.floor(diffSec / 60);
    return `${mins} min.`;
}

function lerp(start, end, t) { return start + (end - start) * t; }
function lerpAngle(start, end, t) {
    let diff = end - start;
    while (diff > 180) diff -= 360;
    while (diff < -180) diff += 360;
    return start + diff * t;
}
function calcDistance(lat1, lon1, lat2, lon2) {
    const R = 6371;
    const dLat = (lat2 - lat1) * Math.PI / 180;
    const dLon = (lon2 - lon1) * Math.PI / 180;
    const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) * Math.sin(dLon/2)**2;
    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}

// ==================== ICONS ====================
const Icons = {
    MapPin: ({ 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.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
        </svg>
    ),
    History: ({ 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 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
        </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>
    ),
    Car: ({ 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 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0" />
        </svg>
    ),
    Play: ({ 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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
        </svg>
    ),
    Pause: ({ 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 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z" />
        </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>
    ),
    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>
    ),
    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>
    ),
    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>
    ),
    Mail: ({ 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 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
        </svg>
    ),
    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>
    ),
    HelpCircle: ({ 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="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
        </svg>
    ),
    Crosshair: ({ className = "w-6 h-6" }) => (
        <svg viewBox="0 0 24 24" className={className} fill="none" stroke="currentColor" strokeWidth="2">
            <path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
        </svg>
    ),
    Layers: ({ 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 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
        </svg>
    ),
    Calendar: ({ 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="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
        </svg>
    ),
    Clock: ({ 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 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
        </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>
    ),
    Check: ({ 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 13l4 4L19 7" />
        </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>
    ),
    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>
    ),
    ChevronDown: ({ 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 9l-7 7-7-7" />
        </svg>
    ),
    Share: ({ 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 12v8a2 2 0 002 2h12a2 2 0 002-2v-8M16 6l-4-4-4 4M12 2v13" />
        </svg>
    ),
    Palette: ({ 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10c1.1 0 2-.9 2-2 0-.53-.21-1.01-.55-1.36-.34-.36-.55-.84-.55-1.37 0-1.1.9-2 2-2h2.34C19.47 15.27 22 12.92 22 10c0-4.42-4.48-8-10-8z" />
            <circle cx="7.5" cy="11.5" r="1.5" fill="currentColor" />
            <circle cx="10.5" cy="7.5" r="1.5" fill="currentColor" />
            <circle cx="15.5" cy="7.5" r="1.5" fill="currentColor" />
            <circle cx="17.5" cy="11.5" r="1.5" fill="currentColor" />
        </svg>
    ),
    CreditCard: ({ 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 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
        </svg>
    ),
    Flame: ({ 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.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M9.879 16.121A3 3 0 1012.015 11L11 14H9c0 .768.293 1.536.879 2.121z" />
        </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>
    ),
    Eye: ({ 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 12a3 3 0 11-6 0 3 3 0 016 0z" />
            <path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
        </svg>
    ),
    Tag: ({ 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 7h.01M7 3h5a1.99 1.99 0 011.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
        </svg>
    ),
    Parking: ({ className = "w-6 h-6" }) => (
        <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="currentColor" viewBox="0 0 24 24">
            <rect x="3" y="3" width="18" height="18" rx="3" fill="none" stroke="currentColor" strokeWidth="2"/>
            <text x="12" y="17" textAnchor="middle" fontSize="14" fontWeight="bold" fill="currentColor">P</text>
        </svg>
    ),
    StopCircle: ({ 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="10" />
            <rect x="9" y="9" width="6" height="6" rx="1" fill="currentColor" />
        </svg>
    ),
    Route: ({ 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" strokeDasharray="3 3" d="M3 17l4-4 4 4 4-4 4 4" />
            <circle cx="5" cy="7" r="2" />
            <circle cx="19" cy="7" r="2" />
        </svg>
    )
};

// ==================== HOOKS ====================
const useNotifications = () => {
    const showNotification = useCallback((message, type = 'info') => {
        const notification = document.createElement('div');
        notification.className = `notification ${type}`;
        notification.innerHTML = `<div class="flex-1">${message}</div>`;
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.classList.add('slide-out');
            setTimeout(() => notification.remove(), 300);
        }, 4000);
    }, []);

    return useMemo(() => ({
        success: (msg) => showNotification(msg, 'success'),
        error: (msg) => showNotification(msg, 'error'),
        info: (msg) => showNotification(msg, 'info')
    }), [showNotification]);
};

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

// ==================== SESSION EXPIRY ====================
let _sessionExpiredFired = false;
function handleSessionExpired() {
    if (_sessionExpiredFired) return;
    _sessionExpiredFired = true;
    window.dispatchEvent(new CustomEvent('session-expired'));
    setTimeout(() => { _sessionExpiredFired = false; }, 3000);
}

function checkAuth(response) {
    if (response.status === 401) { handleSessionExpired(); return true; }
    return false;
}

// ==================== API SERVICE ====================
const apiService = {
    async sendLoginCode(email) {
        try {
            const response = await fetch(`${CONFIG.API_URL}/auth/request-code`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email })
            });
            return await response.json();
        } catch (error) {
            console.error('Error sending code:', error);
            return { success: false, error: error.message };
        }
    },

    async verifyCode(email, code) {
        try {
            const response = await fetch(`${CONFIG.API_URL}/auth/verify-code`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email, code })
            });
            return await response.json();
        } catch (error) {
            console.error('Error verifying code:', error);
            return { success: false, error: error.message };
        }
    },

    async getDevices(token) {
        try {
            const response = await fetch(`${CONFIG.API_URL}/devices`, {
                headers: { 'Authorization': `Bearer ${token}` }
            });
            if (checkAuth(response)) return { success: false, devices: [], expired: true };
            return await response.json();
        } catch (error) {
            console.error('Error fetching devices:', error);
            return { success: false, devices: [] };
        }
    },

    async getPositions(token, imei, from, to) {
        try {
            const response = await fetch(
                `${CONFIG.API_URL}/positions/imei/${imei}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&limit=15000&address=false`,
                { headers: { 'Authorization': `Bearer ${token}` } }
            );
            if (checkAuth(response)) return { success: false, positions: [], expired: true };
            return await response.json();
        } catch (error) {
            console.error('Error fetching positions:', error);
            return { success: false, positions: [] };
        }
    },

    async getPositionsByImei(token, imei, from, to, limit = 5000) {
        try {
            const response = await fetch(
                `${CONFIG.API_URL}/positions/imei/${imei}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&limit=${limit}&address=false`,
                { headers: { 'Authorization': `Bearer ${token}` } }
            );
            if (checkAuth(response)) return { success: false, positions: [], expired: true };
            return await response.json();
        } catch (error) {
            console.error('Error fetching positions by imei:', error);
            return { success: false, positions: [] };
        }
    }
};

// ==================== APP PROVIDER ====================
const AppProvider = ({ children }) => {
    const [user, setUser] = useState(() => {
        const savedUser = localStorage.getItem('easytrack_client_user');
        const savedToken = localStorage.getItem('easytrack_client_token');
        if (savedUser && savedToken) {
            return { ...JSON.parse(savedUser), token: savedToken };
        }
        return null;
    });

    const [currentView, setCurrentView] = useState('live');
    const [devices, setDevices] = useState([]);
    const [selectedDevice, setSelectedDevice] = useState(null);
    const [loading, setLoading] = useState(false);
    const [wsConnected, setWsConnected] = useState(false);
    const [markerStyle, setMarkerStyle] = useState(() => localStorage.getItem('client_marker_style') || 'vehicle');
    const [badgeLabel, setBadgeLabel] = useState(() => localStorage.getItem('client_badge_label') || 'plate');
    const [mapType, setMapType] = useState(() => localStorage.getItem('client_map_type') || 'google');
    const [mapTint, setMapTint] = useState(() => localStorage.getItem('client_map_tint') || 'grayDark');

    const notifications = useNotifications();
    const [hideHeader, setHideHeader] = useState(false);

    const [navCounter, setNavCounter] = useState(0);
    const pendingDeepLink = useRef(null);
    const pendingFocusDevice = useRef(null);

    const handleUrlParams = useCallback(() => {
        const params = new URLSearchParams(window.location.search);
        const magicToken = params.get('magic');
        if (magicToken) {
            window.history.replaceState({}, '', window.location.pathname);
            fetch(`${CONFIG.API_URL}/auth/magic-link`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ token: magicToken })
            })
            .then(async r => {
                const data = await r.json().catch(() => ({}));
                return { ok: r.ok, status: r.status, data };
            })
            .then(({ ok, status, data }) => {
                if (ok && data.success && data.token && data.user) {
                    localStorage.setItem('easytrack_client_user', JSON.stringify(data.user));
                    localStorage.setItem('easytrack_client_token', data.token);
                    setUser({ ...data.user, token: data.token });
                    notifications.success('Connexion réussie');
                } else if (ok && data.requires_2fa) {
                    notifications.info('2FA requise - veuillez vous connecter avec votre code 2FA');
                } else {
                    const msg = data?.error || (status === 401
                        ? 'Lien expiré ou déjà utilisé - veuillez en demander un nouveau'
                        : 'Erreur de connexion par lien magique');
                    notifications.error(msg);
                }
            })
            .catch(err => {
                console.error('Magic link error:', err);
                notifications.error('Erreur réseau - veuillez réessayer');
            });
            return true;
        }
        const promoParam = params.get('promo');
        if (promoParam) {
            localStorage.setItem('easytrack_pending_promo', promoParam.toUpperCase());
            window.history.replaceState({}, '', window.location.pathname);
        }
        const deviceParam = params.get('device');
        const viewParam = params.get('view');
        if (deviceParam || viewParam) {
            pendingDeepLink.current = { device: deviceParam, view: viewParam };
            window.history.replaceState({}, '', window.location.pathname);
        }
        return false;
    }, [notifications]);

    useEffect(() => {
        handleUrlParams();
        const onPageShow = () => handleUrlParams();
        const onVisChange = () => { if (document.visibilityState === 'visible') handleUrlParams(); };
        window.addEventListener('pageshow', onPageShow);
        document.addEventListener('visibilitychange', onVisChange);
        return () => {
            window.removeEventListener('pageshow', onPageShow);
            document.removeEventListener('visibilitychange', onVisChange);
        };
    }, []);

    const [promoToast, setPromoToast] = useState(null);

    useEffect(() => {
        if (!user?.token) return;
        const pending = localStorage.getItem('easytrack_pending_promo');
        if (!pending) return;
        localStorage.removeItem('easytrack_pending_promo');
        fetch(`${CONFIG.API_URL}/affiliates/apply-code`, {
            method: 'POST',
            headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' },
            body: JSON.stringify({ code: pending })
        })
        .then(r => r.json())
        .then(d => {
            if (d.success) {
                setPromoToast({ type: 'success', text: `Votre code ${pending} a bien été activé ! Votre prochaine activation sera gratuite.` });
            } else {
                setPromoToast({ type: 'error', text: d.error || 'Code invalide.' });
            }
            setTimeout(() => setPromoToast(null), 6000);
        })
        .catch(() => {});
    }, [user]);

    const navigate = useCallback((view) => {
        setCurrentView(view);
        setHideHeader(false);
        setNavCounter(c => c + 1);
    }, []);

    const login = useCallback((userData, token) => {
        const userWithToken = { ...userData, token };
        setUser(userWithToken);
        localStorage.setItem('easytrack_client_user', JSON.stringify(userData));
        localStorage.setItem('easytrack_client_token', token);
        notifications.success('Connexion réussie');
    }, [notifications]);

    const logout = useCallback((message) => {
        const token = user?.token;
        const playerId = localStorage.getItem('easytrack_push_player_id');
        if (token && playerId) {
            fetch(`${CONFIG.API_URL}/auth/push-token`, {
                method: 'DELETE',
                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
                body: JSON.stringify({ player_id: playerId })
            }).catch(() => {});
        }
        setUser(null);
        setDevices([]);
        setSelectedDevice(null);
        localStorage.removeItem('easytrack_client_user');
        localStorage.removeItem('easytrack_client_token');
        localStorage.removeItem('easytrack_push_player_id');
        notifications.info(message || 'Déconnexion réussie');
    }, [user, notifications]);

    useEffect(() => {
        const onExpired = () => logout('Votre session a expiré, veuillez vous reconnecter');
        window.addEventListener('session-expired', onExpired);
        return () => window.removeEventListener('session-expired', onExpired);
    }, [logout]);

    const loadDevices = useCallback(async () => {
        if (!user?.token) return;
        setLoading(true);
        try {
            const result = await apiService.getDevices(user.token);
            if (result.success && result.devices) {
                const mapped = result.devices.map(d => ({
                    ...d,
                    latitude: d.last_latitude || d.latitude,
                    longitude: d.last_longitude || d.longitude,
                    speed: d.last_speed ?? d.speed ?? 0,
                    heading: d.last_heading ?? d.heading ?? 0,
                    plate: d.vehicle_plate || d.plate || d.name,
                    vehicle_type: d.vehicle_type || null,
                    vehicle_model: d.vehicle_model || null,
                    timestamp: d.last_position_at || d.last_seen_at || d.timestamp,
                    is_shared: !!d.is_shared,
                    ignition: d.last_ignition ?? null,
                    movement: d.last_movement ?? null,
                    external_voltage: d.last_external_voltage ?? null,
                    odometer: d.last_odometer ?? null,
                    ignition_off_since: d.last_ignition === 0 && d.last_ignition_on_at ? d.last_ignition_on_at : null
                }));
                setDevices(mapped);
                if (mapped.length > 0 && !selectedDevice) {
                    setSelectedDevice(mapped[0]);
                }
            }
        } catch (error) {
            console.error('Error loading devices:', error);
        }
        setLoading(false);
    }, [user, selectedDevice]);

    useEffect(() => {
        if (user?.token) {
            loadDevices();
        }
    }, [user]);

    useEffect(() => {
        if (!devices.length || !pendingDeepLink.current) return;
        const { device, view } = pendingDeepLink.current;
        pendingDeepLink.current = null;
        if (device) {
            const target = devices.find(d => d.imei === device || d.plate === device);
            if (target) {
                setSelectedDevice(target);
                pendingFocusDevice.current = target;
            }
        }
        if (view && ['live', 'replay', 'hotspots', 'stats', 'compte'].includes(view)) {
            setCurrentView(view);
            setNavCounter(c => c + 1);
        }
    }, [devices]);

    // Keep selectedDevice in sync with devices array (WS/polling updates)
    useEffect(() => {
        if (!selectedDevice || !devices.length) return;
        const updated = devices.find(d => d.imei === selectedDevice.imei);
        if (updated && (updated.latitude !== selectedDevice.latitude || updated.longitude !== selectedDevice.longitude || updated.speed !== selectedDevice.speed || updated.heading !== selectedDevice.heading || updated.timestamp !== selectedDevice.timestamp)) {
            setSelectedDevice(updated);
        }
    }, [devices]);

    // Expose global deep link handler for native iOS calls (no reload)
    useEffect(() => {
        window.handleDeepLink = (deviceImei, view) => {
            if (deviceImei && devices.length) {
                const target = devices.find(d => d.imei === deviceImei || d.plate === deviceImei);
                if (target) {
                    setSelectedDevice(target);
                    pendingFocusDevice.current = target;
                }
            }
            if (view && ['live', 'replay', 'hotspots', 'stats', 'compte'].includes(view)) {
                setCurrentView(view);
                setNavCounter(c => c + 1);
            }
        };
        return () => { delete window.handleDeepLink; };
    }, [devices]);

    const changeMarkerStyle = useCallback((style) => {
        setMarkerStyle(style);
        localStorage.setItem('client_marker_style', style);
    }, []);

    const toggleBadgeLabel = useCallback(() => {
        const next = badgeLabel === 'plate' ? 'name' : 'plate';
        setBadgeLabel(next);
        localStorage.setItem('client_badge_label', next);
    }, [badgeLabel]);

    const changeMapType = useCallback((type) => {
        setMapType(type);
        localStorage.setItem('client_map_type', type);
    }, []);

    const changeMapTint = useCallback((tint) => {
        setMapTint(tint);
        localStorage.setItem('client_map_tint', tint);
    }, []);

    // Analytics tracking
    const sessionIdRef = useRef(() => {
        let sid = sessionStorage.getItem('et_session_id');
        if (!sid) { sid = Date.now().toString(36) + Math.random().toString(36).slice(2); sessionStorage.setItem('et_session_id', sid); }
        return sid;
    });
    const sessionStartRef = useRef(Date.now());
    const pagesViewedRef = useRef(0);

    const getDeviceInfo = useCallback(() => {
        const ua = navigator.userAgent;
        let device_type = 'desktop';
        if (/Mobi|Android/i.test(ua)) device_type = /Tablet|iPad/i.test(ua) ? 'tablet' : 'mobile';
        let browser = 'other';
        if (/Chrome/i.test(ua) && !/Edg/i.test(ua)) browser = 'Chrome';
        else if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) browser = 'Safari';
        else if (/Firefox/i.test(ua)) browser = 'Firefox';
        else if (/Edg/i.test(ua)) browser = 'Edge';
        let os = 'other';
        if (/Windows/i.test(ua)) os = 'Windows';
        else if (/Mac/i.test(ua)) os = 'macOS';
        else if (/Android/i.test(ua)) os = 'Android';
        else if (/iPhone|iPad/i.test(ua)) os = 'iOS';
        else if (/Linux/i.test(ua)) os = 'Linux';
        return { device_type, browser, os, screen_width: screen.width, screen_height: screen.height };
    }, []);

    const trackEvent = useCallback(async (event_type, event_data) => {
        if (!user?.token) return;
        try {
            const sid = typeof sessionIdRef.current === 'function' ? sessionIdRef.current() : sessionIdRef.current;
            await fetch(`${CONFIG.API_URL}/analytics/track`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}` },
                body: JSON.stringify({ event_type, event_data, session_id: sid, ...getDeviceInfo() })
            });
        } catch (e) { /* silent */ }
    }, [user, getDeviceInfo]);

    const syncSession = useCallback(async () => {
        if (!user?.token) return;
        try {
            const sid = typeof sessionIdRef.current === 'function' ? sessionIdRef.current() : sessionIdRef.current;
            await fetch(`${CONFIG.API_URL}/analytics/session`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}` },
                body: JSON.stringify({ session_id: sid, duration_ms: Date.now() - sessionStartRef.current, pages_viewed: pagesViewedRef.current, ...getDeviceInfo() })
            });
        } catch (e) { /* silent */ }
    }, [user, getDeviceInfo]);

    // Register OneSignal push token with backend
    const registerPushToken = useCallback(() => {
        if (!user?.token) return;
        const params = new URLSearchParams(window.location.search);
        const playerId = params.get('onesignal_push_id') || window.onesignalplayerid;
        if (!playerId || playerId.length < 10) return;
        const stored = localStorage.getItem('easytrack_push_player_id');
        if (stored === playerId) return;
        fetch(`${CONFIG.API_URL}/auth/push-token`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}` },
            body: JSON.stringify({ player_id: playerId, platform: 'ios' })
        }).then(r => r.json()).then(data => {
            if (data.success) localStorage.setItem('easytrack_push_player_id', playerId);
        }).catch(() => {});
    }, [user?.token]);

    useEffect(() => {
        if (!user?.token) return;
        registerPushToken();
        const retries = [3000, 6000, 10000, 20000];
        const timers = retries.map(ms => setTimeout(registerPushToken, ms));
        const onVisChange = () => { if (document.visibilityState === 'visible') registerPushToken(); };
        document.addEventListener('visibilitychange', onVisChange);
        return () => { timers.forEach(clearTimeout); document.removeEventListener('visibilitychange', onVisChange); };
    }, [user?.token, registerPushToken]);

    // Track app open
    useEffect(() => {
        if (!user?.token) return;
        trackEvent('app_open');
        const interval = setInterval(syncSession, 60000);
        const handleVisChange = () => { if (document.visibilityState === 'hidden') syncSession(); };
        document.addEventListener('visibilitychange', handleVisChange);
        return () => { clearInterval(interval); document.removeEventListener('visibilitychange', handleVisChange); syncSession(); };
    }, [user?.token]);

    // Track page/view changes
    useEffect(() => {
        if (!user?.token) return;
        pagesViewedRef.current++;
        trackEvent('page_view', { page: currentView });
    }, [currentView, user?.token]);

    const value = {
        user, login, logout,
        currentView, navigate, navCounter,
        devices, setDevices, loadDevices,
        selectedDevice, setSelectedDevice,
        loading, setLoading,
        wsConnected, setWsConnected,
        markerStyle, changeMarkerStyle,
        badgeLabel, toggleBadgeLabel,
        mapType, changeMapType,
        mapTint, changeMapTint,
        hideHeader, setHideHeader,
        notifications,
        registerPushToken,
        pendingFocusDevice
    };

    return <AppContext.Provider value={value}>
        {children}
        {promoToast && (
            <div className="fixed top-4 left-4 right-4 z-[9999] animate-slide-down">
                <div className={`mx-auto max-w-sm rounded-2xl shadow-xl p-4 flex items-start gap-3 ${promoToast.type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white'}`}>
                    <div className="w-8 h-8 rounded-full bg-white/20 flex items-center justify-center flex-shrink-0 mt-0.5">
                        {promoToast.type === 'success'
                            ? <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/></svg>
                            : <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
                        }
                    </div>
                    <div className="flex-1 min-w-0">
                        <p className="text-sm font-semibold">{promoToast.text}</p>
                    </div>
                    <button onClick={() => setPromoToast(null)} className="text-white/70 hover:text-white flex-shrink-0">
                        <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
                    </button>
                </div>
            </div>
        )}
    </AppContext.Provider>;
};

// ==================== AUTH MODAL ====================
const AuthModal = ({ onClose }) => {
    const { login, notifications } = useAppContext();
    const [email, setEmail] = useState('');
    const [code, setCode] = useState(['', '', '', '', '', '']);
    const [isCodeSent, setIsCodeSent] = useState(false);
    const [loading, setLoading] = useState(false);
    const [countdown, setCountdown] = useState(0);
    const inputRefs = useRef([]);

    useEffect(() => {
        if (countdown > 0) {
            const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
            return () => clearTimeout(timer);
        }
    }, [countdown]);

    const handleSendCode = async () => {
        if (!email || !email.includes('@')) {
            notifications.error('Veuillez entrer un email valide');
            return;
        }
        setLoading(true);
        const result = await apiService.sendLoginCode(email);
        setLoading(false);
        
        if (result.success) {
            setIsCodeSent(true);
            setCountdown(60);
            notifications.success('Code envoyé par email');
        } else {
            notifications.error(result.error || 'Erreur lors de l\'envoi du code');
        }
    };

    const handleCodeChange = (index, value) => {
        if (!/^\d*$/.test(value)) return;
        
        const newCode = [...code];
        newCode[index] = value.slice(-1);
        setCode(newCode);

        if (value && index < 5) {
            inputRefs.current[index + 1]?.focus();
        }

        if (newCode.every(d => d) && newCode.join('').length === 6) {
            handleVerifyCode(newCode.join(''));
        }
    };

    const handleKeyDown = (index, e) => {
        if (e.key === 'Backspace' && !code[index] && index > 0) {
            inputRefs.current[index - 1]?.focus();
        }
    };

    const handlePaste = (e) => {
        e.preventDefault();
        const pastedData = e.clipboardData.getData('text').replace(/\D/g, '').slice(0, 6);
        if (pastedData.length === 6) {
            const newCode = pastedData.split('');
            setCode(newCode);
            handleVerifyCode(pastedData);
        }
    };

    const handleVerifyCode = async (codeStr) => {
        setLoading(true);
        const result = await apiService.verifyCode(email, codeStr);
        setLoading(false);

        if (result.success && result.token) {
            login(result.user || { email }, result.token);
            onClose();
        } else {
            notifications.error(result.error || 'Code invalide');
            setCode(['', '', '', '', '', '']);
            inputRefs.current[0]?.focus();
        }
    };

    return (
        <div style={{
            position: 'fixed', inset: 0, zIndex: 1000,
            background: 'rgb(37, 99, 235)',
            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
            padding: '2rem'
        }}>
            <div style={{ width: '100%', maxWidth: '360px' }}>
                <div className="text-center" style={{ marginBottom: '2.5rem' }}>
                    <img src="assets/logotype/Easytrack-logotype-wt.svg" alt="EasyTrack" style={{ height: '44px', margin: '0 auto 1.5rem', filter: 'drop-shadow(0 4px 20px rgba(0,0,0,0.2))' }} />
                    <p style={{ color: 'rgba(255,255,255,0.8)', fontSize: '0.95rem' }}>
                        {isCodeSent ? 'Entrez le code reçu par email' : 'Connectez-vous pour suivre vos véhicules'}
                    </p>
                </div>

                {!isCodeSent ? (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                        <input
                            type="email"
                            value={email}
                            onChange={(e) => setEmail(e.target.value)}
                            onKeyDown={(e) => { if (e.key === 'Enter' && email) handleSendCode(); }}
                            placeholder="votre@email.ch"
                            autoFocus
                            className="auth-input"
                            style={{
                                width: '100%', padding: '0.875rem 1rem',
                                background: 'rgba(255,255,255,0.15)', border: '2px solid rgba(255,255,255,0.3)',
                                borderRadius: '0.75rem', fontSize: '1rem', color: 'white',
                                outline: 'none', backdropFilter: 'blur(4px)',
                                boxSizing: 'border-box'
                            }}
                        />
                        <button
                            onClick={handleSendCode}
                            disabled={loading || !email}
                            style={{
                                width: '100%', padding: '0.875rem',
                                background: loading || !email ? 'rgba(255,255,255,0.2)' : 'white',
                                color: loading || !email ? 'rgba(255,255,255,0.5)' : 'rgb(37, 99, 235)',
                                border: 'none', borderRadius: '0.75rem',
                                fontWeight: 700, fontSize: '1rem', cursor: loading || !email ? 'not-allowed' : 'pointer',
                                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem',
                                transition: 'all 0.2s', boxShadow: loading || !email ? 'none' : '0 4px 15px rgba(0,0,0,0.15)'
                            }}
                        >
                            {loading ? <div className="spinner" style={{width: '1.25rem', height: '1.25rem', borderColor: 'rgba(37,99,235,0.2)', borderTopColor: 'rgb(37,99,235)'}}></div> : null}
                            Recevoir le code
                        </button>
                    </div>
                ) : (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem' }}>
                        <div style={{ display: 'flex', gap: '0.5rem', justifyContent: 'center' }} onPaste={handlePaste}>
                            {code.map((digit, index) => (
                                <input
                                    key={index}
                                    ref={el => inputRefs.current[index] = el}
                                    type="text"
                                    inputMode="numeric"
                                    maxLength={1}
                                    value={digit}
                                    onChange={(e) => handleCodeChange(index, e.target.value)}
                                    onKeyDown={(e) => handleKeyDown(index, e)}
                                    autoFocus={index === 0}
                                    style={{
                                        width: '2.75rem', height: '3.25rem', textAlign: 'center',
                                        background: 'rgba(255,255,255,0.15)', border: '2px solid rgba(255,255,255,0.4)',
                                        borderRadius: '0.625rem', fontSize: '1.5rem', fontWeight: 700,
                                        color: 'white', outline: 'none', caretColor: 'white',
                                        transition: 'all 0.15s'
                                    }}
                                    onFocus={(e) => { e.target.style.borderColor = 'white'; e.target.style.background = 'rgba(255,255,255,0.25)'; }}
                                    onBlur={(e) => { e.target.style.borderColor = 'rgba(255,255,255,0.4)'; e.target.style.background = 'rgba(255,255,255,0.15)'; }}
                                />
                            ))}
                        </div>
                        
                        <div className="text-center" style={{ fontSize: '0.875rem', color: 'rgba(255,255,255,0.7)' }}>
                            {countdown > 0 ? (
                                <span>Renvoyer le code dans {countdown}s</span>
                            ) : (
                                <button onClick={handleSendCode} style={{ color: 'white', background: 'none', border: 'none', cursor: 'pointer', textDecoration: 'underline', fontSize: '0.875rem' }}>
                                    Renvoyer le code
                                </button>
                            )}
                        </div>

                        <button
                            onClick={() => { setIsCodeSent(false); setCode(['', '', '', '', '', '']); }}
                            style={{
                                width: '100%', padding: '0.875rem',
                                background: 'rgba(255,255,255,0.15)', color: 'white',
                                border: '1px solid rgba(255,255,255,0.3)', borderRadius: '0.75rem',
                                fontWeight: 600, fontSize: '0.9rem', cursor: 'pointer',
                                transition: 'all 0.2s'
                            }}
                        >
                            Changer d'email
                        </button>
                    </div>
                )}
            </div>
        </div>
    );
};

// ==================== LIVE VIEW ====================
const LiveView = () => {
    const { user, devices, setDevices, selectedDevice, setSelectedDevice, loadDevices, wsConnected, setWsConnected, markerStyle, badgeLabel, toggleBadgeLabel, mapType, changeMapType: ctxChangeType, mapTint, changeMapTint: ctxChangeTint, pendingFocusDevice } = useAppContext();
    const [showAuthModal, setShowAuthModal] = useState(false);
    const [showMapTypeMenu, setShowMapTypeMenu] = useState(false);
    const [showTintMenu, setShowTintMenu] = useState(false);
    const [showTrail, setShowTrail] = useState(() => localStorage.getItem('client_show_trail') === 'true');
    const [showMyPosition, setShowMyPosition] = useState(() => localStorage.getItem('client_show_my_position') !== 'false');
    const [showLiveOverlay, setShowLiveOverlay] = useState(false);
    const [showAllDevices, setShowAllDevices] = useState(false);
    const showAllDevicesRef = useRef(false);
    const [deviceAddresses, setDeviceAddresses] = useState({});
    const addressCacheRef = useRef({});
    const liveToolbarRef = useRef(null);
    const mapRef = useRef(null);
    const mapInstance = useRef(null);
    const tileLayerRef = useRef(null);
    const markersRef = useRef({});
    const badgesRef = useRef({});
    const markerLastPosRef = useRef({});
    const markerAnimsRef = useRef({});
    const wsRef = useRef(null);
    const selectedDeviceRef = useRef(selectedDevice);
    const markerStyleRef = useRef(markerStyle);
    const badgeLabelRef = useRef(badgeLabel);
    const trailsRef = useRef({});
    const trailPolylinesRef = useRef({});
    const showTrailRef = useRef(showTrail);
    const autoFitDoneRef = useRef(false);
    const myPosMarkerRef = useRef(null);
    const myPosRef = useRef(null);
    const geoWatchRef = useRef(null);

    useEffect(() => { selectedDeviceRef.current = selectedDevice; }, [selectedDevice]);
    useEffect(() => { markerStyleRef.current = markerStyle; }, [markerStyle]);
    useEffect(() => { badgeLabelRef.current = badgeLabel; }, [badgeLabel]);
    useEffect(() => { showTrailRef.current = showTrail; }, [showTrail]);

    useEffect(() => {
        if (!showMapTypeMenu && !showTintMenu && !showLiveOverlay) return;
        const handler = (e) => {
            if (liveToolbarRef.current && !liveToolbarRef.current.contains(e.target)) {
                setShowMapTypeMenu(false); setShowTintMenu(false); setShowLiveOverlay(false);
            }
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [showMapTypeMenu, showTintMenu, showLiveOverlay]);

    useEffect(() => {
        if (!showMyPosition || !navigator.geolocation) {
            if (myPosMarkerRef.current && mapInstance.current) {
                mapInstance.current.removeLayer(myPosMarkerRef.current);
                myPosMarkerRef.current = null;
            }
            if (geoWatchRef.current !== null) { navigator.geolocation.clearWatch(geoWatchRef.current); geoWatchRef.current = null; }
            return;
        }
        const updateMyPos = (pos) => {
            const lat = pos.coords.latitude, lng = pos.coords.longitude;
            myPosRef.current = { latitude: lat, longitude: lng };
            if (!mapInstance.current) return;
            const icon = L.divIcon({
                html: `<div style="display:flex;align-items:center;gap:5px;">
                    <div style="width:16px;height:16px;border-radius:50%;background:#4285F4;border:3px solid white;box-shadow:0 1px 4px rgba(0,0,0,0.3);flex-shrink:0;"></div>
                    <div style="background:white;border-radius:4px;padding:1px 5px;font-size:11px;font-weight:700;color:#4285F4;box-shadow:0 1px 4px rgba(0,0,0,0.2);white-space:nowrap;">Moi</div>
                </div>`,
                className: '', iconSize: null, iconAnchor: [8, 8]
            });
            if (myPosMarkerRef.current) {
                myPosMarkerRef.current.setLatLng([lat, lng]);
            } else {
                myPosMarkerRef.current = L.marker([lat, lng], { icon, zIndexOffset: -200, interactive: false }).addTo(mapInstance.current);
            }
        };
        geoWatchRef.current = navigator.geolocation.watchPosition(updateMyPos, () => {}, { enableHighAccuracy: true, maximumAge: 10000 });
        return () => {
            if (geoWatchRef.current !== null) { navigator.geolocation.clearWatch(geoWatchRef.current); geoWatchRef.current = null; }
        };
    }, [showMyPosition]);

    const toggleMyPosition = () => {
        const v = !showMyPosition;
        setShowMyPosition(v);
        localStorage.setItem('client_show_my_position', v.toString());
    };

    const fitVehicleAndMe = () => {
        if (!mapInstance.current) return;
        mapInstance.current.invalidateSize();
        const bounds = [];
        if (selectedDevice?.latitude && selectedDevice?.longitude) bounds.push([selectedDevice.latitude, selectedDevice.longitude]);
        if (myPosRef.current) bounds.push([myPosRef.current.latitude, myPosRef.current.longitude]);
        if (bounds.length === 2) {
            mapInstance.current.fitBounds(L.latLngBounds(bounds), { padding: [60, 60], maxZoom: 15 });
        } else if (bounds.length === 1) {
            mapInstance.current.setView(bounds[0], 15, { animate: true });
        }
    };

    useEffect(() => {
        setShowAuthModal(!user);
    }, [user]);

    const applyMapTint = useCallback((tint) => {
        if (!mapInstance.current) return;
        const tilePane = mapInstance.current.getContainer().querySelector('.leaflet-tile-pane');
        if (tilePane) tilePane.style.filter = CONFIG.MAP_TINTS[tint]?.filter || 'none';
    }, []);

    const changeMapType = useCallback((type) => {
        if (!mapInstance.current) return;
        ctxChangeType(type);
        if (tileLayerRef.current) mapInstance.current.removeLayer(tileLayerRef.current);
        const layer = CONFIG.TILE_LAYERS[type] || CONFIG.TILE_LAYERS.google;
        tileLayerRef.current = L.tileLayer(layer.url, { attribution: layer.attribution }).addTo(mapInstance.current);
        applyMapTint(mapTint);
        setShowMapTypeMenu(false);
    }, [mapTint, applyMapTint, ctxChangeType]);

    const changeMapTint = useCallback((tint) => {
        ctxChangeTint(tint);
        applyMapTint(tint);
        setShowTintMenu(false);
    }, [applyMapTint, ctxChangeTint]);

    // Initialize map + auto-fit (re-runs when user logs in and map div appears)
    useEffect(() => {
        if (!mapRef.current || mapInstance.current) return;
        const savedType = localStorage.getItem('client_map_type') || 'google';
        const savedTint = localStorage.getItem('client_map_tint') || 'standard';
        const initialLayer = CONFIG.TILE_LAYERS[savedType] || CONFIG.TILE_LAYERS.google;
        const map = L.map(mapRef.current, { zoomControl: false }).setView(CONFIG.MAP_CENTER, CONFIG.MAP_ZOOM);
        tileLayerRef.current = L.tileLayer(initialLayer.url, { attribution: initialLayer.attribution }).addTo(map);
        mapInstance.current = map;
        const tilePane = map.getContainer().querySelector('.leaflet-tile-pane');
        if (tilePane) tilePane.style.filter = CONFIG.MAP_TINTS[savedTint]?.filter || 'none';

        // Fix: le container peut grandir/changer de taille après création (header skeleton -> header réel,
        // changement d'orientation, clavier mobile, etc.). Sans invalidateSize Leaflet garde la taille
        // initiale et le setView centre par rapport à l'ancienne taille -> véhicule décalé.
        const ro = new ResizeObserver(() => {
            if (!mapInstance.current) return;
            mapInstance.current.invalidateSize();
            // Si on n'a pas encore fait l'auto-center initial, ou si c'est la 1re fois que la map
            // atteint une taille non nulle, on re-centre sur le véhicule sélectionné.
            const sel = selectedDeviceRef.current;
            if (sel?.latitude && sel?.longitude && !autoFitDoneRef.current && !showAllDevicesRef.current) {
                mapInstance.current.setView([sel.latitude, sel.longitude], 15);
                autoFitDoneRef.current = true;
            }
        });
        ro.observe(mapRef.current);

        // Premier coup de invalidateSize après le 1er paint (au cas où ResizeObserver ne tirerait pas tout de suite)
        requestAnimationFrame(() => {
            if (mapInstance.current) mapInstance.current.invalidateSize();
        });

        return () => { ro.disconnect(); map.remove(); mapInstance.current = null; };
    }, [user]);

    // Build marker icon HTML (mirrors admin logic)
    const buildMarkerIcon = useCallback((heading, color, isSelected, speed, vehicleType) => {
        const rotation = heading || 0;
        const style = markerStyleRef.current;
        const borderColor = isSelected ? '#3B82F6' : 'white';

        if (style === 'vehicle') {
            const vw = isSelected ? 22 : 18;
            const vh = getVehicleSize(vehicleType, vw);
            const iconW = vw + 4;
            const iconH = vh + 4;
            const html = `<div style="width:${iconW}px;height:${iconH}px;display:flex;align-items:center;justify-content:center;cursor:pointer;transform:rotate(${rotation}deg);filter:drop-shadow(0 2px 3px rgba(0,0,0,0.4));">${getVehicleImg(vehicleType, vw)}</div>`;
            return L.divIcon({ html, className: 'custom-marker', iconSize: [iconW, iconH], iconAnchor: [iconW/2, iconH/2] });
        } else {
            const iconW = isSelected ? 44 : 36;
            const html = `<div style="background:${color};width:${iconW}px;height:${iconW}px;border-radius:50%;border:${isSelected?'4px':'3px'} solid ${borderColor};box-shadow:0 2px 8px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;cursor:pointer;"><svg viewBox="0 0 24 24" width="18" height="18" fill="white" style="transform:rotate(${rotation}deg);"><polygon points="12,2 22,22 12,17 2,22"/></svg></div>`;
            return L.divIcon({ html, className: 'custom-marker', iconSize: [iconW, iconW], iconAnchor: [iconW/2, iconW/2] });
        }
    }, []);

    // Build badge HTML (plate or name + speed/parking time)
    const buildBadgeIcon = useCallback((device, status) => {
        let badgeHtml = '';
        const label = badgeLabelRef.current === 'name'
            ? (device.vehicle_model || device.name || device.plate || device.imei.slice(-6))
            : (device.plate || device.name || device.imei.slice(-6));
        badgeHtml += `<div class="plate-badge">${label}</div>`;

        const spd = device.speed !== undefined ? device.speed : 0;
        if (spd > 3) {
            badgeHtml += `<div class="speed-badge" style="background:#22c55e">${Math.round(spd)} km/h</div>`;
        } else {
            let diffMin = 999;
            const ts = device.timestamp || device.last_seen_at;
            if (ts) {
                let d = String(ts).replace(' ', 'T');
                if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
                diffMin = (new Date() - new Date(d)) / 1000 / 60;
                if (diffMin < 0) diffMin = 0;
            }
            if (diffMin >= 5) {
                const parkDur = formatParkingDuration(ts);
                badgeHtml += `<div class="speed-badge parked-badge"><span style="font-weight:800;font-size:10px;">P</span>${parkDur || ''}</div>`;
            } else {
                const stopDur = formatStopDuration(ts);
                badgeHtml += `<div class="speed-badge" style="background:#F97316">${stopDur || '0 sec.'}</div>`;
            }
        }

        return L.divIcon({
            html: `<div class="plate-speed-container">${badgeHtml}</div>`,
            className: 'plate-badge-container',
            iconSize: [100, 40],
            iconAnchor: [-22, 20]
        });
    }, []);

    const easeInOut = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    const positionQueueRef = useRef({});
    const lastWsTimeRef = useRef({});

    const updateTrail = useCallback((imei, lat, lng, color, replaceLastPoint = false) => {
        if (!mapInstance.current) return;
        if (!trailsRef.current[imei]) trailsRef.current[imei] = [];
        const trail = trailsRef.current[imei];
        if (replaceLastPoint && trail.length > 0) {
            trail[trail.length - 1] = [lat, lng];
        } else {
            const last = trail.length > 0 ? trail[trail.length - 1] : null;
            if (!last || last[0] !== lat || last[1] !== lng) {
                trail.push([lat, lng]);
                if (trail.length > 10) trail.shift();
            }
        }
        if (trail.length >= 2) {
            if (trailPolylinesRef.current[imei]) {
                trailPolylinesRef.current[imei].setLatLngs(trail);
                trailPolylinesRef.current[imei].setStyle({ color });
            } else {
                trailPolylinesRef.current[imei] = L.polyline(trail, {
                    color, weight: 3, opacity: 0.6, dashArray: '5, 10'
                }).addTo(mapInstance.current);
            }
        }
    }, []);

    const animateMarkerTo = useCallback((markerId, marker, targetLat, targetLng, targetHeading, color, isSelected, imei, speed, vehicleType) => {
        if (markerAnimsRef.current[markerId]) {
            cancelAnimationFrame(markerAnimsRef.current[markerId]);
            markerAnimsRef.current[markerId] = null;
        }
        const cur = marker.getLatLng();
        const startLat = cur.lat, startLng = cur.lng;
        const lastPos = markerLastPosRef.current[markerId] || { heading: 0 };
        const startHeading = lastPos.heading || 0;
        const distance = calcDistance(startLat, startLng, targetLat, targetLng) * 1000;

        // Very small movement: snap immediately (no animation needed)
        if (distance < 3) {
            marker.setLatLng([targetLat, targetLng]);
            marker.setIcon(buildMarkerIcon(targetHeading, color, isSelected, speed, vehicleType));
            markerLastPosRef.current[markerId] = { lat: targetLat, lng: targetLng, heading: targetHeading };
            if (badgesRef.current[imei]) badgesRef.current[imei].setLatLng([targetLat, targetLng]);
            if (showTrailRef.current) updateTrail(imei, targetLat, targetLng, color);
            return;
        }

        // Large jump (> 500m): snap instantly to avoid long weird animations
        if (distance > 500) {
            marker.setLatLng([targetLat, targetLng]);
            marker.setIcon(buildMarkerIcon(targetHeading, color, isSelected, speed, vehicleType));
            markerLastPosRef.current[markerId] = { lat: targetLat, lng: targetLng, heading: targetHeading };
            if (badgesRef.current[imei]) badgesRef.current[imei].setLatLng([targetLat, targetLng]);
            if (showTrailRef.current) updateTrail(imei, targetLat, targetLng, color);
            return;
        }

        // Calculate animation duration based on actual WS interval
        const now = performance.now();
        const lastTime = lastWsTimeRef.current[markerId] || now - 3000;
        const interval = Math.max(now - lastTime, 1000);
        lastWsTimeRef.current[markerId] = now;
        // Duration = ~90% of the interval between updates, clamped 1-6s
        const duration = Math.min(Math.max(interval * 0.9, 1000), 6000);

        // Add a trail point that will follow the animation
        let trailAdded = false;
        if (showTrailRef.current) {
            updateTrail(imei, startLat, startLng, color);
            trailAdded = true;
        }

        const startTime = now;
        const updateIcon = speed > 5;
        if (!updateIcon) {
            marker.setIcon(buildMarkerIcon(targetHeading, color, isSelected, speed, vehicleType));
        }
        function animate(currentTime) {
            const elapsed = currentTime - startTime;
            const rawProgress = Math.min(elapsed / duration, 1);
            const progress = easeInOut(rawProgress);
            const lat = lerp(startLat, targetLat, progress);
            const lng = lerp(startLng, targetLng, progress);
            const heading = updateIcon ? lerpAngle(startHeading, targetHeading, progress) : targetHeading;
            marker.setLatLng([lat, lng]);
            if (updateIcon) marker.setIcon(buildMarkerIcon(heading, color, isSelected, speed, vehicleType));
            if (badgesRef.current[imei]) badgesRef.current[imei].setLatLng([lat, lng]);
            markerLastPosRef.current[markerId] = { lat, lng, heading };
            if (isSelected && mapInstance.current && !showAllDevicesRef.current) mapInstance.current.panTo([lat, lng], { animate: true, duration: 0.3 });
            if (trailAdded && showTrailRef.current) updateTrail(imei, lat, lng, color, true);
            if (rawProgress >= 1) { markerAnimsRef.current[markerId] = null; return; }
            markerAnimsRef.current[markerId] = requestAnimationFrame(animate);
        }
        markerAnimsRef.current[markerId] = requestAnimationFrame(animate);
    }, [buildMarkerIcon, updateTrail]);

    const clearTrails = useCallback(() => {
        Object.keys(trailPolylinesRef.current).forEach(imei => {
            if (trailPolylinesRef.current[imei]) trailPolylinesRef.current[imei].remove();
        });
        trailPolylinesRef.current = {};
        trailsRef.current = {};
    }, []);

    const toggleShowTrail = useCallback(() => {
        const v = !showTrailRef.current;
        setShowTrail(v);
        localStorage.setItem('client_show_trail', v.toString());
        if (!v) clearTrails();
    }, [clearTrails]);

    // Update markers: show all devices or only selected
    const updateMarkers = useCallback((deviceList) => {
        if (!mapInstance.current) return;
        const curSelected = selectedDeviceRef.current;
        const selectedImei = curSelected?.imei;
        const allMode = showAllDevicesRef.current;

        const visibleImeis = new Set();
        if (allMode) {
            deviceList.forEach(d => { if (d.latitude && d.longitude) visibleImeis.add(d.imei); });
        } else if (selectedImei) {
            visibleImeis.add(selectedImei);
        }

        // Remove markers/badges for devices no longer visible
        Object.keys(markersRef.current).forEach(markerId => {
            const imei = markerId.replace('imei-', '');
            if (!visibleImeis.has(imei)) {
                markersRef.current[markerId].remove();
                delete markersRef.current[markerId];
                delete markerLastPosRef.current[markerId];
                if (markerAnimsRef.current[markerId]) {
                    cancelAnimationFrame(markerAnimsRef.current[markerId]);
                    delete markerAnimsRef.current[markerId];
                }
            }
        });
        Object.keys(badgesRef.current).forEach(imei => {
            if (!visibleImeis.has(imei)) {
                badgesRef.current[imei].remove();
                delete badgesRef.current[imei];
            }
        });

        if (visibleImeis.size === 0) return;

        // Add/update markers for all visible devices
        deviceList.forEach(device => {
            if (!visibleImeis.has(device.imei) || !device.latitude || !device.longitude) return;
            const markerId = 'imei-' + device.imei;
            const posStatus = getDeviceStatus(device.speed || 0, device.timestamp || device.last_seen_at, device.is_active, device.ignition, device.movement);
            const color = posStatus.colors.main;
            const heading = device.heading || 0;

            const badgeIcon = buildBadgeIcon(device, posStatus);
            if (badgesRef.current[device.imei]) {
                badgesRef.current[device.imei].setLatLng([device.latitude, device.longitude]);
                badgesRef.current[device.imei].setIcon(badgeIcon);
            } else {
                badgesRef.current[device.imei] = L.marker([device.latitude, device.longitude], { icon: badgeIcon, interactive: false }).addTo(mapInstance.current);
            }

            if (markersRef.current[markerId]) {
                markersRef.current[markerId].deviceData = device;
                animateMarkerTo(markerId, markersRef.current[markerId], device.latitude, device.longitude, heading, color, device.imei === selectedImei || allMode, device.imei, device.speed, device.vehicle_type);
            } else {
                const icon = buildMarkerIcon(heading, color, device.imei === selectedImei || allMode, device.speed, device.vehicle_type);
                const marker = L.marker([device.latitude, device.longitude], { icon }).addTo(mapInstance.current);
                marker.deviceData = device;
                marker.on('click', () => {
                    setSelectedDevice(device);
                    setShowAllDevices(false);
                    showAllDevicesRef.current = false;
                });
                markerLastPosRef.current[markerId] = { lat: device.latitude, lng: device.longitude, heading };
                markersRef.current[markerId] = marker;
            }
        });
    }, [buildMarkerIcon, buildBadgeIcon, animateMarkerTo]);

    // Update marker when devices or selected device change
    useEffect(() => {
        updateMarkers(devices);
    }, [devices, selectedDevice, markerStyle, badgeLabel, showAllDevices]);

    // Center map and clear trails when switching vehicle; reset all-devices mode
    const prevSelectedRef = useRef(selectedDevice?.imei);
    useEffect(() => {
        if (!selectedDevice?.imei || selectedDevice.imei === prevSelectedRef.current) { prevSelectedRef.current = selectedDevice?.imei; return; }
        prevSelectedRef.current = selectedDevice.imei;
        if (showAllDevicesRef.current) {
            showAllDevicesRef.current = false;
            setShowAllDevices(false);
        }
        clearTrails();
        if (mapInstance.current && selectedDevice.latitude && selectedDevice.longitude) {
            mapInstance.current.invalidateSize();
            mapInstance.current.setView([selectedDevice.latitude, selectedDevice.longitude], 15, { animate: true });
        }
    }, [selectedDevice, clearTrails]);

    // Deep link focus: center map on device after deep link navigation
    useEffect(() => {
        if (!pendingFocusDevice?.current || !mapInstance.current) return;
        const dev = pendingFocusDevice.current;
        pendingFocusDevice.current = null;
        if (dev.latitude && dev.longitude) {
            showAllDevicesRef.current = false;
            setShowAllDevices(false);
            setTimeout(() => {
                if (mapInstance.current) mapInstance.current.setView([dev.latitude, dev.longitude], 15, { animate: true });
            }, 500);
        }
    }, [selectedDevice, devices]);

    // Geocode device positions for address display
    useEffect(() => {
        if (!devices.length) return;
        devices.forEach(device => {
            if (!device.latitude || !device.longitude) return;
            const key = `${device.latitude.toFixed(4)},${device.longitude.toFixed(4)}`;
            if (addressCacheRef.current[key]) {
                setDeviceAddresses(prev => prev[device.imei] === addressCacheRef.current[key] ? prev : { ...prev, [device.imei]: addressCacheRef.current[key] });
                return;
            }
            fetch(`${CONFIG.API_URL}/geocode?latitude=${device.latitude}&longitude=${device.longitude}`, {
                headers: { Authorization: `Bearer ${user.token}` }
            }).then(r => { if (checkAuth(r)) return null; return r.json(); })
                .then(data => {
                    if (!data) return;
                    const addr = data.address || '';
                    addressCacheRef.current[key] = addr;
                    setDeviceAddresses(prev => ({ ...prev, [device.imei]: addr }));
                })
                .catch(() => {});
        });
    }, [devices]);

    // Auto-center on selected device
    useEffect(() => {
        if (!mapInstance.current || !selectedDevice?.latitude || !selectedDevice?.longitude) return;
        if (!autoFitDoneRef.current) {
            mapInstance.current.invalidateSize();
            mapInstance.current.setView([selectedDevice.latitude, selectedDevice.longitude], 15);
            autoFitDoneRef.current = true;
        }
    }, [selectedDevice]);

    // Backfill: load last 30s positions for selected device so marker moves immediately
    const backfillImeiRef = useRef(null);
    useEffect(() => {
        if (!user?.token || !selectedDevice?.imei || !mapInstance.current) return;
        if (backfillImeiRef.current === selectedDevice.imei) return;
        backfillImeiRef.current = selectedDevice.imei;
        const imei = selectedDevice.imei;
        const backfill = async () => {
            const now = new Date();
            const ago = new Date(now.getTime() - 30000);
            const fromStr = ago.toISOString().slice(0, 19).replace('T', ' ');
            const toStr = now.toISOString().slice(0, 19).replace('T', ' ');
            try {
                const resp = await fetch(`${CONFIG.API_URL}/positions/imei/${imei}?from=${encodeURIComponent(fromStr)}&to=${encodeURIComponent(toStr)}&limit=20&address=false`, {
                    headers: { Authorization: `Bearer ${user.token}` }
                });
                if (checkAuth(resp)) return;
                if (resp.ok) {
                    const data = await resp.json();
                    const pts = (data.positions || []).reverse();
                    if (pts.length > 1) {
                        const last = pts[pts.length - 1];
                        setDevices(prev => prev.map(d => {
                            if (d.imei !== imei) return d;
                            return { ...d, latitude: last.latitude, longitude: last.longitude, speed: last.speed ?? d.speed, heading: last.heading ?? d.heading, timestamp: last.timestamp || d.timestamp };
                        }));
                    }
                }
            } catch (e) {}
        };
        setTimeout(backfill, 300);
    }, [user, selectedDevice?.imei]);

    // WebSocket connection
    useEffect(() => {
        if (!user?.token || !devices.length) return;
        const connectWs = () => {
            try {
                const ws = new WebSocket(CONFIG.WS_URL);
                wsRef.current = ws;
                ws.onopen = () => {
                    setWsConnected(true);
                    ws.send(JSON.stringify({ action: 'subscribe', imeis: devices.map(d => d.imei) }));
                };
                ws.onmessage = (event) => {
                    try {
                        const msg = JSON.parse(event.data);
                        const pos = (msg.type === 'position' && msg.data) ? msg.data : null;
                        if (pos && pos.imei) {
                            const lat = pos.lat || pos.latitude;
                            const lng = pos.lng || pos.longitude;
                            if (!lat || !lng) return;
                            const speed = pos.speed ?? 0;

                            setDevices(prev => prev.map(d => {
                                if (d.imei !== pos.imei) return d;
                                if (d.latitude && d.longitude) {
                                    const dist = calcDistance(d.latitude, d.longitude, lat, lng) * 1000;
                                    // GPS glitch: ignore teleports > 2km in a single update
                                    if (dist > 2000) {
                                        return { ...d, speed, timestamp: pos.ts || pos.timestamp || d.timestamp, last_seen_at: pos.ts || d.last_seen_at };
                                    }
                                    // GPS noise: ignore tiny movements when nearly stopped
                                    if (speed <= 3 && dist < 8) {
                                        return { ...d, speed, timestamp: pos.ts || pos.timestamp || d.timestamp, last_seen_at: pos.ts || d.last_seen_at };
                                    }
                                }
                                return {
                                    ...d,
                                    latitude: lat,
                                    longitude: lng,
                                    speed,
                                    heading: pos.heading ?? d.heading,
                                    ignition: pos.ignition ?? d.ignition,
                                    movement: pos.movement ?? d.movement,
                                    external_voltage: pos.external_voltage ?? d.external_voltage,
                                    odometer: pos.odometer ?? d.odometer,
                                    timestamp: pos.ts || pos.timestamp || pos.server_timestamp || new Date().toISOString(),
                                    last_seen_at: pos.ts || pos.server_timestamp || new Date().toISOString()
                                };
                            }));
                        }
                    } catch (e) {}
                };
                ws.onclose = () => { setWsConnected(false); setTimeout(connectWs, 5000); };
                ws.onerror = () => setWsConnected(false);
            } catch (e) { console.error('WebSocket error:', e); }
        };
        connectWs();
        return () => { if (wsRef.current) wsRef.current.close(); };
    }, [user, devices.length]);

    // Polling fallback every 10 seconds
    useEffect(() => {
        if (!user?.token) return;
        const interval = setInterval(loadDevices, 10000);
        return () => clearInterval(interval);
    }, [user, loadDevices]);

    const fitAllDevices = () => {
        if (!mapInstance.current) return;
        mapInstance.current.invalidateSize();
        if (showAllDevicesRef.current) {
            const validDevices = devices.filter(d => d.latitude && d.longitude);
            if (validDevices.length === 0) return;
            if (validDevices.length === 1) {
                mapInstance.current.setView([validDevices[0].latitude, validDevices[0].longitude], 15, { animate: true });
            } else {
                const bounds = L.latLngBounds(validDevices.map(d => [d.latitude, d.longitude]));
                mapInstance.current.fitBounds(bounds, { padding: [50, 50], animate: true });
            }
        } else if (selectedDevice?.latitude && selectedDevice?.longitude) {
            mapInstance.current.setView([selectedDevice.latitude, selectedDevice.longitude], 15, { animate: true });
        }
    };

    const toggleAllDevicesOnMap = () => {
        const next = !showAllDevicesRef.current;
        showAllDevicesRef.current = next;
        setShowAllDevices(next);
        if (next && mapInstance.current) {
            const validDevices = devices.filter(d => d.latitude && d.longitude);
            if (validDevices.length === 0) return;
            if (validDevices.length === 1) {
                mapInstance.current.setView([validDevices[0].latitude, validDevices[0].longitude], 15, { animate: true });
            } else {
                const bounds = L.latLngBounds(validDevices.map(d => [d.latitude, d.longitude]));
                mapInstance.current.fitBounds(bounds, { padding: [50, 50], animate: true });
            }
        } else if (!next && mapInstance.current && selectedDevice?.latitude && selectedDevice?.longitude) {
            mapInstance.current.setView([selectedDevice.latitude, selectedDevice.longitude], 15, { animate: true });
        }
    };

    const focusDevice = (device) => {
        setSelectedDevice(device);
        showAllDevicesRef.current = false;
        setShowAllDevices(false);
        if (mapInstance.current && device.latitude && device.longitude) mapInstance.current.setView([device.latitude, device.longitude], 15);
    };

    if (!user) {
        return (
            <AuthModal onClose={() => {}} />
        );
    }

    return (
        <div className="h-full flex flex-col">
            <div className="flex-1 relative">
                <div ref={mapRef} className="map-container"></div>

                {/* Toolbar */}
                <div ref={liveToolbarRef} className="absolute top-4 right-4 flex flex-col gap-2 z-[1000]">
                    <button onClick={toggleAllDevicesOnMap}
                        className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center transition-colors ${showAllDevices ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}
                        title={showAllDevices ? 'Afficher un véhicule' : 'Afficher tous les véhicules'}>
                        {showAllDevices ? (
                            <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
                                <circle cx="6" cy="6" r="2.5" />
                                <circle cx="12" cy="6" r="2.5" />
                                <circle cx="18" cy="6" r="2.5" />
                                <circle cx="6" cy="12" r="2.5" />
                                <circle cx="12" cy="12" r="2.5" />
                                <circle cx="18" cy="12" r="2.5" />
                                <circle cx="6" cy="18" r="2.5" />
                                <circle cx="12" cy="18" r="2.5" />
                                <circle cx="18" cy="18" r="2.5" />
                            </svg>
                        ) : (
                            <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
                                <circle cx="12" cy="12" r="4" />
                            </svg>
                        )}
                    </button>
                    <button onClick={fitAllDevices} className="w-10 h-10 bg-white rounded-lg shadow-lg flex items-center justify-center hover:bg-gray-50" title="Centrer sur le véhicule">
                        <Icons.Crosshair className="w-5 h-5 text-gray-700" />
                    </button>
                    <button onClick={fitVehicleAndMe} className="w-10 h-10 bg-white rounded-lg shadow-lg flex items-center justify-center hover:bg-gray-50" title="Véhicule + Ma position">
                        <svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5 text-gray-700" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
                            <circle cx="12" cy="11" r="3" fill="currentColor" stroke="none" />
                        </svg>
                    </button>
                    <div className="relative">
                        <button onClick={() => { setShowMapTypeMenu(!showMapTypeMenu); setShowTintMenu(false); setShowLiveOverlay(false); }}
                            className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showMapTypeMenu ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                            <Icons.Layers className="w-5 h-5" />
                        </button>
                        {showMapTypeMenu && (
                            <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-44 map-dropdown">
                                <div className="px-3 py-1 text-xs font-semibold text-gray-400 uppercase">Type de carte</div>
                                {Object.entries(CONFIG.TILE_LAYERS).map(([key, layer]) => (
                                    <button key={key} onClick={() => changeMapType(key)}
                                        className={`w-full px-3 py-2 text-sm text-left flex items-center gap-2 ${mapType === key ? 'text-blue-600 bg-blue-50 font-semibold' : 'text-gray-700 hover:bg-gray-50'}`}>
                                        {mapType === key ? <span className="w-2 h-2 rounded-full bg-blue-500"></span> : <span className="w-2 h-2"></span>}
                                        {layer.label}
                                    </button>
                                ))}
                            </div>
                        )}
                    </div>
                    <div className="relative">
                        <button onClick={() => { setShowTintMenu(!showTintMenu); setShowMapTypeMenu(false); setShowLiveOverlay(false); }}
                            className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showTintMenu ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                            <Icons.Palette className="w-5 h-5" />
                        </button>
                        {showTintMenu && (
                            <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-44 map-dropdown">
                                <div className="px-3 py-1 text-xs font-semibold text-gray-400 uppercase">Teinte</div>
                                {Object.entries(CONFIG.MAP_TINTS).map(([key, tint]) => (
                                    <button key={key} onClick={() => changeMapTint(key)}
                                        className={`w-full px-3 py-2 text-sm text-left flex items-center gap-2.5 ${mapTint === key ? 'text-blue-600 bg-blue-50 font-semibold' : 'text-gray-700 hover:bg-gray-50'}`}>
                                        <span className={`w-5 h-5 rounded-full flex-shrink-0 ${mapTint === key ? 'ring-2 ring-blue-500 ring-offset-1' : 'ring-1 ring-gray-200'}`} style={{ background: tint.preview }}></span>
                                        {tint.label}
                                    </button>
                                ))}
                            </div>
                        )}
                    </div>
                    <div className="relative">
                        <button onClick={() => { setShowLiveOverlay(!showLiveOverlay); setShowMapTypeMenu(false); setShowTintMenu(false); }}
                            className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showLiveOverlay ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                            <Icons.Eye className="w-5 h-5" />
                        </button>
                        {showLiveOverlay && (
                            <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-52 map-dropdown z-[1001]">
                                <div className="px-3 py-1.5 text-xs font-semibold text-gray-400 uppercase">Affichage</div>
                                <button onClick={toggleMyPosition}
                                    className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                    <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${showMyPosition ? 'bg-blue-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                        <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                    </div>
                                    <div className="flex items-center gap-2">
                                        <div style={{ width: 16, height: 16, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                                            <div style={{ width: 10, height: 10, borderRadius: '50%', background: '#4285F4', border: '2px solid white', boxShadow: '0 0 0 1px #4285F4' }}></div>
                                        </div>
                                        <span className="font-medium text-gray-700">Ma position</span>
                                    </div>
                                </button>
                                <button onClick={toggleShowTrail}
                                    className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                    <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${showTrail ? 'bg-green-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                        <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                    </div>
                                    <div className="flex items-center gap-2">
                                        <svg width="16" height="16" viewBox="0 0 16 16"><line x1="0" y1="8" x2="16" y2="8" stroke="#22C55E" strokeWidth="2.5" strokeDasharray="3 3"/></svg>
                                        <span className="font-medium text-gray-700">Tracé pointillé</span>
                                    </div>
                                </button>
                                <div className="border-t border-gray-100 my-1"></div>
                                <div className="px-3 py-1.5 text-xs font-semibold text-gray-400 uppercase">Étiquette</div>
                                <button onClick={toggleBadgeLabel}
                                    className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                    <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${badgeLabel === 'name' ? 'bg-blue-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                        <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                    </div>
                                    <div className="flex items-center gap-2">
                                        <Icons.Tag className="w-4 h-4 text-gray-500" />
                                        <span className="font-medium text-gray-700">{badgeLabel === 'plate' ? 'Plaque' : 'Nom'}</span>
                                    </div>
                                </button>
                            </div>
                        )}
                    </div>
                </div>
            </div>

            {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
        </div>
    );
};

// ==================== MAP TOOLBAR (reusable) ====================
const MapToolbar = ({ mapInstance, tileLayerRef, extra }) => {
    const { mapType, changeMapType: ctxChangeType, mapTint, changeMapTint: ctxChangeTint } = useAppContext();
    const [showType, setShowType] = useState(false);
    const [showTint, setShowTint] = useState(false);
    const toolbarRef = useRef(null);

    useEffect(() => {
        if (!showType && !showTint) return;
        const handler = (e) => {
            if (toolbarRef.current && !toolbarRef.current.contains(e.target)) {
                setShowType(false); setShowTint(false);
            }
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [showType, showTint]);

    const applyTint = (tint) => {
        if (!mapInstance?.current) return;
        const tp = mapInstance.current.getContainer().querySelector('.leaflet-tile-pane');
        if (tp) tp.style.filter = CONFIG.MAP_TINTS[tint]?.filter || 'none';
    };
    const doChangeType = (type) => {
        if (!mapInstance?.current) return;
        ctxChangeType(type);
        if (tileLayerRef?.current) mapInstance.current.removeLayer(tileLayerRef.current);
        const l = CONFIG.TILE_LAYERS[type] || CONFIG.TILE_LAYERS.google;
        tileLayerRef.current = L.tileLayer(l.url, { attribution: l.attribution }).addTo(mapInstance.current);
        applyTint(mapTint); setShowType(false);
    };
    const doChangeTint = (tint) => {
        ctxChangeTint(tint);
        applyTint(tint); setShowTint(false);
    };

    return (
        <div ref={toolbarRef} className="absolute top-4 right-4 flex flex-col gap-2 z-[1000]">
            {extra}
            <div className="relative">
                <button onClick={() => { setShowType(!showType); setShowTint(false); }}
                    className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showType ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                    <Icons.Layers className="w-5 h-5" />
                </button>
                {showType && (
                    <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-44 map-dropdown">
                        <div className="px-3 py-1 text-xs font-semibold text-gray-400 uppercase">Type de carte</div>
                        {Object.entries(CONFIG.TILE_LAYERS).map(([k, l]) => (
                            <button key={k} onClick={() => doChangeType(k)}
                                className={`w-full px-3 py-2 text-sm text-left flex items-center gap-2 ${mapType === k ? 'text-blue-600 bg-blue-50 font-semibold' : 'text-gray-700 hover:bg-gray-50'}`}>
                                {mapType === k ? <span className="w-2 h-2 rounded-full bg-blue-500"></span> : <span className="w-2 h-2"></span>}
                                {l.label}
                            </button>
                        ))}
                    </div>
                )}
            </div>
            <div className="relative">
                <button onClick={() => { setShowTint(!showTint); setShowType(false); }}
                    className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showTint ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                    <Icons.Palette className="w-5 h-5" />
                </button>
                {showTint && (
                    <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-44 map-dropdown">
                        <div className="px-3 py-1 text-xs font-semibold text-gray-400 uppercase">Teinte</div>
                        {Object.entries(CONFIG.MAP_TINTS).map(([k, t]) => (
                            <button key={k} onClick={() => doChangeTint(k)}
                                className={`w-full px-3 py-2 text-sm text-left flex items-center gap-2.5 ${mapTint === k ? 'text-blue-600 bg-blue-50 font-semibold' : 'text-gray-700 hover:bg-gray-50'}`}>
                                <span className={`w-5 h-5 rounded-full flex-shrink-0 ${mapTint === k ? 'ring-2 ring-blue-500 ring-offset-1' : 'ring-1 ring-gray-200'}`} style={{ background: t.preview }}></span>
                                {t.label}
                            </button>
                        ))}
                    </div>
                )}
            </div>
        </div>
    );
};

// ==================== VEHICLE SELECTOR ====================
const VehicleSelector = ({ devices, selectedDevice, onSelect, label }) => {
    const [open, setOpen] = useState(false);
    const selectorRef = useRef(null);
    const selDev = selectedDevice;

    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]);

    return (
        <div ref={selectorRef} className="relative">
            <button onClick={() => setOpen(!open)}
                className="flex items-center gap-2 bg-white rounded-lg shadow-lg px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 max-w-[200px]">
                <Icons.Car className="w-4 h-4 text-blue-500 flex-shrink-0" />
                <span className="truncate">{selDev ? (selDev.plate || selDev.name || selDev.imei.slice(-6)) : (label || 'Véhicule')}</span>
                <svg className={`w-4 h-4 flex-shrink-0 transition-transform ${open ? 'rotate-180' : ''}`} 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="absolute left-0 top-full mt-1 bg-white rounded-xl shadow-xl border border-gray-100 py-1 w-56 map-dropdown z-[1001] max-h-64 overflow-y-auto">
                    <div className="px-3 py-1 text-xs font-semibold text-gray-400 uppercase">Mes véhicules</div>
                    {devices.map(d => (
                        <button key={d.imei} onClick={() => { onSelect(d); setOpen(false); }}
                            className={`w-full px-3 py-2 text-sm text-left flex items-center gap-2 ${selDev?.imei === d.imei ? 'text-blue-600 bg-blue-50 font-semibold' : 'text-gray-700 hover:bg-gray-50'}`}>
                            <div className="w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0" style={{ backgroundColor: getDeviceStatus(d.speed||0, d.timestamp||d.last_seen_at, d.is_active, d.ignition, d.movement).colors.main }}>
                                <Icons.Car className="w-3 h-3 text-white" />
                            </div>
                            <span className="truncate">{d.plate || d.name || d.imei.slice(-6)}</span>
                            {d.is_shared && <span className="text-xs bg-blue-100 text-blue-600 px-1 rounded">Partagé</span>}
                        </button>
                    ))}
                </div>
            )}
        </div>
    );
};

// ==================== INIT MAP HELPER ====================
function initMapWithPrefs(container, { forceType, forceTint } = {}) {
    const mapType = forceType || localStorage.getItem('client_map_type') || 'google';
    const mapTint = forceTint || localStorage.getItem('client_map_tint') || 'standard';
    const initialLayer = CONFIG.TILE_LAYERS[mapType] || CONFIG.TILE_LAYERS.google;
    const map = L.map(container, { zoomControl: false }).setView(CONFIG.MAP_CENTER, CONFIG.MAP_ZOOM);
    const tileLayer = L.tileLayer(initialLayer.url, { attribution: initialLayer.attribution }).addTo(map);
    const tilePane = map.getContainer().querySelector('.leaflet-tile-pane');
    if (tilePane) tilePane.style.filter = CONFIG.MAP_TINTS[mapTint]?.filter || 'none';
    return { map, tileLayer };
}

// ==================== REPLAY VIEW ====================
const ReplayView = () => {
    const { user, devices, selectedDevice, setSelectedDevice, markerStyle, badgeLabel, toggleBadgeLabel, currentView, navCounter, setHideHeader } = useAppContext();
    const [showAuthModal, setShowAuthModal] = useState(false);
    const [periodPreset, setPeriodPreset] = useState('today');
    const [showDateRange, setShowDateRange] = useState(false);
    const [dateStart, setDateStart] = useState('');
    const [dateEnd, setDateEnd] = useState('');
    const [timeStart, setTimeStart] = useState('00:00');
    const [timeEnd, setTimeEnd] = useState('23:59');
    const [positions, setPositions] = useState([]);
    const [isPlaying, setIsPlaying] = useState(false);
    const [playbackSpeed, setPlaybackSpeed] = useState(0.5);
    const [currentIndex, setCurrentIndex] = useState(0);
    const [loading, setLoading] = useState(false);
    const [isFollowing, setIsFollowing] = useState(false);

    const [showOverlayMenu, setShowOverlayMenu] = useState(false);
    const replayOverlayRef = useRef(null);
    useEffect(() => {
        if (!showOverlayMenu) return;
        const handler = (e) => {
            if (replayOverlayRef.current && !replayOverlayRef.current.contains(e.target)) setShowOverlayMenu(false);
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [showOverlayMenu]);
    const [showParkings, setShowParkings] = useState(() => localStorage.getItem('client_replay_parkings') !== 'false');
    const [showStops, setShowStops] = useState(() => localStorage.getItem('client_replay_stops') !== 'false');
    const [showDashedTrail, setShowDashedTrail] = useState(() => localStorage.getItem('client_replay_dashed') === 'true');
    const [tripEvents, setTripEvents] = useState([]);
    const [currentAddress, setCurrentAddress] = useState('');
    const [replayTab, setReplayTab] = useState('trajets');
    const [mapReturnTab, setMapReturnTab] = useState(null);
    const [activeParkingInfo, setActiveParkingInfo] = useState(null);
    const [mapViewAddress, setMapViewAddress] = useState('');
    useEffect(() => {
        if (currentView === 'replay') setReplayTab('trajets');
    }, [navCounter]);
    
    const geocodeTimerRef = useRef(null);
    const geocodeCacheRef = useRef({});

    const mapRef = useRef(null);
    const mapInstance = useRef(null);
    const tileLayerRef = useRef(null);
    const markerRef = useRef(null);
    const polylineRef = useRef(null);
    const polylineSegmentsRef = useRef([]);
    const playIntervalRef = useRef(null);
    const parkingMarkersRef = useRef([]);
    const stopMarkersRef = useRef([]);
    const startEndMarkersRef = useRef([]);
    const replayBadgeRef = useRef(null);

    const formatDateLocal = (date) => {
        const y = date.getFullYear();
        const m = (date.getMonth() + 1).toString().padStart(2, '0');
        const d = date.getDate().toString().padStart(2, '0');
        return `${y}-${m}-${d}`;
    };
    const formatTimeLocal = (date) =>
        date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');

    const applyPeriodPreset = useCallback((preset) => {
        setPeriodPreset(preset);
        if (preset !== 'custom') setShowDateRange(false);
        const now = new Date();
        const today = formatDateLocal(now);
        switch (preset) {
            case 'today':
                setDateStart(today); setDateEnd(today);
                setTimeStart('00:00'); setTimeEnd('23:59');
                break;
            case 'yesterday': {
                const yest = formatDateLocal(new Date(now.getTime() - 86400000));
                setDateStart(yest); setDateEnd(yest);
                setTimeStart('00:00'); setTimeEnd('23:59');
                break;
            }
            case 'daybeforeyesterday': {
                const dbf = formatDateLocal(new Date(now.getTime() - 2 * 86400000));
                setDateStart(dbf); setDateEnd(dbf);
                setTimeStart('00:00'); setTimeEnd('23:59');
                break;
            }
            case 'custom':
                setShowDateRange(true);
                break;
        }
    }, []);

    useEffect(() => { setShowAuthModal(!user); }, [user]);

    useEffect(() => {
        applyPeriodPreset('today');
    }, []);

    useEffect(() => {
        if (!mapRef.current || mapInstance.current) return;
        const { map, tileLayer } = initMapWithPrefs(mapRef.current, { forceType: 'google', forceTint: 'grayDark' });
        mapInstance.current = map;
        tileLayerRef.current = tileLayer;
        map.on('dragstart', () => setIsFollowing(false));
        return () => { map.remove(); mapInstance.current = null; };
    }, []);

    const calculateTrips = useCallback((pts) => {
        if (!pts || pts.length < 2) { setTripEvents([]); return; }
        const events = [];
        let currentParking = null;
        const stopThreshold = 2 * 60 * 1000;
        const hasIgnition = pts.some(p => p.ignition !== null && p.ignition !== undefined);

        for (let i = 0; i < pts.length; i++) {
            const pos = pts[i];
            const ts = new Date(pos.timestamp || pos.server_timestamp);
            const isParked = hasIgnition ? pos.ignition === 0 : ((pos.speed || 0) <= 0);

            if (!isParked) {
                if (currentParking) {
                    currentParking.endTime = ts;
                    currentParking.duration = currentParking.endTime - currentParking.startTime;
                    if (currentParking.duration >= stopThreshold) {
                        events.push({ type: 'parking', data: currentParking });
                    } else if (currentParking.duration >= 30000) {
                        events.push({ type: 'stop', data: currentParking });
                    }
                    currentParking = null;
                }
            } else {
                if (!currentParking) {
                    currentParking = {
                        startTime: ts, endTime: ts,
                        latitude: pos.latitude, longitude: pos.longitude,
                        address: pos.address || null, duration: 0
                    };
                } else {
                    currentParking.endTime = ts;
                }
            }
        }
        if (currentParking) {
            currentParking.duration = currentParking.endTime - currentParking.startTime;
            if (currentParking.duration >= stopThreshold) {
                events.push({ type: 'parking', data: currentParking });
            }
        }
        setTripEvents(events);
    }, []);

    const clearParkingMarkers = useCallback(() => {
        parkingMarkersRef.current.forEach(m => { if (m && mapInstance.current) mapInstance.current.removeLayer(m); });
        parkingMarkersRef.current = [];
    }, []);

    const drawParkingMarkers = useCallback(() => {
        if (!mapInstance.current) return;
        clearParkingMarkers();
        const parkings = tripEvents.filter(e => e.type === 'parking');
        parkings.forEach(event => {
            const p = event.data;
            if (!p.latitude || !p.longitude) return;
            const dur = p.duration || 0;
            const hrs = Math.floor(dur / 3600000);
            const mins = Math.floor((dur % 3600000) / 60000);
            const txt = hrs > 0 ? `${hrs}h${mins > 0 ? `${String(mins).padStart(2, '0')}min` : ''}` : `${mins}min`;
            const icon = L.divIcon({
                html: `<div style="display:flex;align-items:center;gap:3px;background:white;border-radius:20px;padding:3px 8px;box-shadow:0 2px 8px rgba(0,0,0,0.2);border:2px solid #3B82F6;white-space:nowrap;"><span style="font-size:14px;">🅿️</span><span style="font-size:11px;font-weight:600;color:#3B82F6;">${txt}</span></div>`,
                className: '', iconSize: [80, 30], iconAnchor: [40, 30]
            });
            const marker = L.marker([p.latitude, p.longitude], { icon, interactive: true, zIndexOffset: -500 }).addTo(mapInstance.current);
            marker.bindTooltip(`<strong>Stationnement</strong><br>Durée: ${txt}`, { direction: 'top', offset: [0, -15] });
            parkingMarkersRef.current.push(marker);
        });
    }, [tripEvents, clearParkingMarkers]);

    const clearStopMarkers = useCallback(() => {
        stopMarkersRef.current.forEach(m => { if (m && mapInstance.current) mapInstance.current.removeLayer(m); });
        stopMarkersRef.current = [];
    }, []);

    const drawStopMarkers = useCallback(() => {
        if (!mapInstance.current) return;
        clearStopMarkers();
        const stops = tripEvents.filter(e => e.type === 'stop');
        stops.forEach(event => {
            const s = event.data;
            if (!s.latitude || !s.longitude) return;
            const dur = s.duration || 0;
            const secs = Math.floor(dur / 1000);
            const txt = secs >= 60 ? `${Math.floor(secs / 60)}min` : `${secs}s`;
            const icon = L.divIcon({
                html: `<div style="display:flex;align-items:center;gap:3px;background:white;border-radius:20px;padding:3px 8px;box-shadow:0 2px 8px rgba(0,0,0,0.2);border:2px solid #F97316;white-space:nowrap;"><span style="font-size:13px;">⏸️</span><span style="font-size:11px;font-weight:600;color:#F97316;">${txt}</span></div>`,
                className: '', iconSize: [60, 28], iconAnchor: [30, 28]
            });
            const marker = L.marker([s.latitude, s.longitude], { icon, interactive: true, zIndexOffset: -500 }).addTo(mapInstance.current);
            marker.bindTooltip(`<strong>Arrêt court</strong><br>Durée: ${txt}`, { direction: 'top', offset: [0, -12] });
            stopMarkersRef.current.push(marker);
        });
    }, [tripEvents, clearStopMarkers]);

    const applyDashedStyle = useCallback((dashed) => {
        polylineSegmentsRef.current.forEach(seg => {
            seg.setStyle({ dashArray: dashed ? '8, 12' : null, weight: dashed ? 3 : 4 });
        });
    }, []);

    useEffect(() => {
        if (tripEvents.length > 0 && showParkings) drawParkingMarkers();
        else clearParkingMarkers();
    }, [tripEvents, showParkings]);

    useEffect(() => {
        if (tripEvents.length > 0 && showStops) drawStopMarkers();
        else clearStopMarkers();
    }, [tripEvents, showStops]);

    useEffect(() => {
        applyDashedStyle(showDashedTrail);
    }, [showDashedTrail]);

    const toggleParkings = () => {
        const v = !showParkings;
        setShowParkings(v);
        localStorage.setItem('client_replay_parkings', v.toString());
    };
    const toggleStops = () => {
        const v = !showStops;
        setShowStops(v);
        localStorage.setItem('client_replay_stops', v.toString());
    };
    const toggleDashedTrail = () => {
        const v = !showDashedTrail;
        setShowDashedTrail(v);
        localStorage.setItem('client_replay_dashed', v.toString());
    };

    const selectedImei = selectedDevice?.imei;
    const loadPositions = useCallback(async () => {
        if (!user?.token || !selectedImei || !dateStart || !dateEnd) return;
        setLoading(true);
        setPositions([]); setCurrentIndex(0); setIsPlaying(false); setTripEvents([]);
        clearParkingMarkers(); clearStopMarkers();
        startEndMarkersRef.current.forEach(m => { try { mapInstance.current?.removeLayer(m); } catch(e) {} });
        startEndMarkersRef.current = [];
        if (polylineRef.current && mapInstance.current) { mapInstance.current.removeLayer(polylineRef.current); polylineRef.current = null; }
        polylineSegmentsRef.current.forEach(seg => { try { mapInstance.current?.removeLayer(seg); } catch(e) {} });
        polylineSegmentsRef.current = [];
        if (markerRef.current && mapInstance.current) { mapInstance.current.removeLayer(markerRef.current); markerRef.current = null; }
        if (replayBadgeRef.current && mapInstance.current) { mapInstance.current.removeLayer(replayBadgeRef.current); replayBadgeRef.current = null; }

        try {
            const localFrom = new Date(`${dateStart}T${timeStart}:00`);
            const localTo = new Date(`${dateEnd}T${timeEnd}:59`);
            const fromStr = localFrom.toISOString().slice(0, 19).replace('T', ' ');
            const toStr = localTo.toISOString().slice(0, 19).replace('T', ' ');

            const resp = await fetch(`${CONFIG.API_URL}/positions/imei/${selectedImei}?from=${encodeURIComponent(fromStr)}&to=${encodeURIComponent(toStr)}&limit=15000&address=false`, {
                headers: { Authorization: `Bearer ${user.token}` }
            });
            if (checkAuth(resp)) { setLoading(false); return; }
            if (resp.ok) {
                const data = await resp.json();
                const pts = (data.positions || []).reverse();
                if (pts.length > 0) {
                    setPositions(pts);
                    drawRoute(pts);
                    calculateTrips(pts);
                }
            }
        } catch (e) { console.error('Replay error:', e); }
        setLoading(false);
    }, [user, selectedImei, dateStart, dateEnd, timeStart, timeEnd, clearParkingMarkers, clearStopMarkers, calculateTrips]);

    useEffect(() => {
        if (replayTab === 'carte' && !mapReturnTab && user?.token && selectedImei && mapInstance.current && dateStart && dateEnd) {
            const t = setTimeout(loadPositions, 200);
            return () => clearTimeout(t);
        }
    }, [replayTab, mapReturnTab, user, selectedImei, dateStart, dateEnd, timeStart, timeEnd, loadPositions]);

    const getGradientColor = (ratio) => {
        const stops = [[0,34,197,94],[0.33,234,179,8],[0.66,249,115,22],[1,239,68,68]];
        let lo = stops[0], hi = stops[stops.length - 1];
        for (let i = 0; i < stops.length - 1; i++) {
            if (ratio >= stops[i][0] && ratio <= stops[i + 1][0]) { lo = stops[i]; hi = stops[i + 1]; break; }
        }
        const t = lo[0] === hi[0] ? 0 : (ratio - lo[0]) / (hi[0] - lo[0]);
        return `rgb(${Math.round(lo[1]+t*(hi[1]-lo[1]))},${Math.round(lo[2]+t*(hi[2]-lo[2]))},${Math.round(lo[3]+t*(hi[3]-lo[3]))})`;
    };

    const drawRoute = (pts) => {
        if (!mapInstance.current || !pts.length) return;
        if (polylineRef.current) { mapInstance.current.removeLayer(polylineRef.current); polylineRef.current = null; }
        polylineSegmentsRef.current.forEach(seg => { try { mapInstance.current.removeLayer(seg); } catch(e) {} });
        polylineSegmentsRef.current = [];
        if (markerRef.current) mapInstance.current.removeLayer(markerRef.current);
        markerRef.current = null;
        if (replayBadgeRef.current) { try { mapInstance.current.removeLayer(replayBadgeRef.current); } catch(e) {} replayBadgeRef.current = null; }

        const dashed = localStorage.getItem('client_replay_dashed') === 'true';
        const segs = [];
        const total = Math.max(1, pts.length - 1);
        for (let i = 0; i < pts.length - 1; i++) {
            const color = getGradientColor(i / total);
            const seg = L.polyline(
                [[pts[i].latitude, pts[i].longitude], [pts[i+1].latitude, pts[i+1].longitude]],
                { color, weight: dashed ? 3 : 4, opacity: 0.7, dashArray: dashed ? '8, 12' : null, lineJoin: 'round', lineCap: 'round' }
            ).addTo(mapInstance.current);
            segs.push(seg);
        }
        polylineSegmentsRef.current = segs;

        const latlngs = pts.map(p => [p.latitude, p.longitude]);
        mapInstance.current.fitBounds(L.latLngBounds(latlngs), { padding: [50, 50] });

        startEndMarkersRef.current.forEach(m => { try { mapInstance.current.removeLayer(m); } catch(e) {} });
        startEndMarkersRef.current = [];

        const startIcon = L.divIcon({
            html: `<div style="width:24px;height:24px;border-radius:50%;background:#22C55E;border:3px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;">
                <svg viewBox="0 0 24 24" width="12" height="12" fill="white"><polygon points="8,5 19,12 8,19"/></svg>
            </div>`,
            className: 'custom-marker', iconSize: [24, 24], iconAnchor: [12, 12]
        });
        const endIcon = L.divIcon({
            html: `<div style="width:24px;height:24px;border-radius:50%;background:#EF4444;border:3px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;">
                <svg viewBox="0 0 24 24" width="12" height="12" fill="white"><rect x="7" y="7" width="10" height="10" rx="1"/></svg>
            </div>`,
            className: 'custom-marker', iconSize: [24, 24], iconAnchor: [12, 12]
        });

        const first = pts[0], last = pts[pts.length - 1];
        const startMarker = L.marker([first.latitude, first.longitude], { icon: startIcon, zIndexOffset: -100 }).addTo(mapInstance.current);
        const endMarker = L.marker([last.latitude, last.longitude], { icon: endIcon, zIndexOffset: -100 }).addTo(mapInstance.current);
        startEndMarkersRef.current = [startMarker, endMarker];

        updateReplayMarker(pts[0]);
    };

    const updateReplayMarker = (pos) => {
        if (!mapInstance.current || !pos) return;
        const style = markerStyle || localStorage.getItem('client_marker_style') || 'vehicle';
        const heading = pos.heading || 0;
        let html, w, h;
        if (style === 'vehicle') {
            const vType = selectedDevice?.vehicle_type || 'Voiture';
            const vw = 20;
            const vh = getVehicleSize(vType, vw);
            w = vw + 4; h = vh + 4;
            html = `<div style="width:${w}px;height:${h}px;display:flex;align-items:center;justify-content:center;transform:rotate(${heading}deg);filter:drop-shadow(0 2px 3px rgba(0,0,0,0.4));">${getVehicleImg(vType, vw)}</div>`;
        } else {
            w = 36; h = 36;
            html = `<div style="background:#3B82F6;width:${w}px;height:${h}px;border-radius:50%;border:3px solid white;box-shadow:0 2px 8px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;"><svg viewBox="0 0 24 24" width="18" height="18" fill="white" style="transform:rotate(${heading}deg);"><polygon points="12,2 22,22 12,17 2,22"/></svg></div>`;
        }
        const icon = L.divIcon({ html, className: 'custom-marker', iconSize: [w, h], iconAnchor: [w/2, h/2] });
        if (markerRef.current) {
            markerRef.current.setLatLng([pos.latitude, pos.longitude]);
            markerRef.current.setIcon(icon);
        } else {
            markerRef.current = L.marker([pos.latitude, pos.longitude], { icon }).addTo(mapInstance.current);
        }

        const label = selectedDevice?.plate || selectedDevice?.name || selectedDevice?.imei?.slice(-6) || '';
        const speed = Math.round(pos.speed || 0);
        const speedColor = speed > 3 ? '#22c55e' : '#F97316';
        const speedText = speed > 3 ? `${speed} km/h` : 'Arrêt';
        let badgeHtml = `<div class="plate-speed-container">`;
        badgeHtml += `<div class="plate-badge">${label}</div>`;
        badgeHtml += `<div class="speed-badge" style="background:${speedColor}">${speedText}</div>`;
        badgeHtml += `</div>`;
        const badgeIcon = L.divIcon({ html: badgeHtml, className: 'plate-badge-container', iconSize: [100, 40], iconAnchor: [-22, 20] });
        if (replayBadgeRef.current) {
            replayBadgeRef.current.setLatLng([pos.latitude, pos.longitude]);
            replayBadgeRef.current.setIcon(badgeIcon);
        } else {
            replayBadgeRef.current = L.marker([pos.latitude, pos.longitude], { icon: badgeIcon, interactive: false }).addTo(mapInstance.current);
        }
    };

    // Playback - speed: 1x=200ms, 2x=100ms, 4x=50ms, 8x=25ms per point
    useEffect(() => {
        if (isPlaying && positions.length > 0) {
            const interval = Math.max(25, 200 / playbackSpeed);
            playIntervalRef.current = setInterval(() => {
                setCurrentIndex(prev => {
                    if (prev >= positions.length - 1) { setIsPlaying(false); return prev; }
                    return prev + 1;
                });
            }, interval);
        } else { clearInterval(playIntervalRef.current); }
        return () => clearInterval(playIntervalRef.current);
    }, [isPlaying, positions.length, playbackSpeed]);

    useEffect(() => {
        if (positions[currentIndex]) {
            updateReplayMarker(positions[currentIndex]);
            if (markerRef.current) {
                const el = markerRef.current.getElement();
                if (el) {
                    if (isPlaying && playbackSpeed <= 1) {
                        const ms = Math.round(200 / playbackSpeed);
                        el.style.transition = `transform ${ms}ms linear`;
                    } else {
                        el.style.transition = '';
                    }
                }
            }
            if (replayBadgeRef.current) {
                const bel = replayBadgeRef.current.getElement();
                if (bel) {
                    if (isPlaying && playbackSpeed <= 1) {
                        const ms = Math.round(200 / playbackSpeed);
                        bel.style.transition = `transform ${ms}ms linear`;
                    } else {
                        bel.style.transition = '';
                    }
                }
            }
            if (isFollowing && mapInstance.current) {
                const dur = (isPlaying && playbackSpeed <= 1) ? (200 / playbackSpeed) / 1000 : 0.3;
                mapInstance.current.panTo([positions[currentIndex].latitude, positions[currentIndex].longitude], { animate: true, duration: dur });
            }
        }
        const segs = polylineSegmentsRef.current;
        if (segs.length > 0 && isPlaying) {
            const WINDOW = 40;
            const FADE = 20;
            for (let i = 0; i < segs.length; i++) {
                const dist = Math.abs(i - currentIndex);
                if (dist <= WINDOW) {
                    segs[i].setStyle({ opacity: 0.9, color: '#3B82F6' });
                } else if (dist <= WINDOW + FADE) {
                    const t = (dist - WINDOW) / FADE;
                    segs[i].setStyle({ opacity: 0.9 - t * 0.55, color: '#9CA3AF' });
                } else {
                    segs[i].setStyle({ opacity: 0.25, color: '#9CA3AF' });
                }
            }
        } else if (segs.length > 0 && !isPlaying) {
            const total = Math.max(1, segs.length);
            for (let i = 0; i < segs.length; i++) {
                segs[i].setStyle({ opacity: 0.7, color: getGradientColor(i / total) });
            }
        }
    }, [currentIndex, positions, isPlaying, playbackSpeed, isFollowing]);

    useEffect(() => {
        const pos = positions[currentIndex];
        if (!pos) { setCurrentAddress(''); return; }
        const key = `${pos.latitude.toFixed(4)},${pos.longitude.toFixed(4)}`;
        if (geocodeCacheRef.current[key]) { setCurrentAddress(geocodeCacheRef.current[key]); return; }
        setCurrentAddress('...');
        if (geocodeTimerRef.current) clearTimeout(geocodeTimerRef.current);
        geocodeTimerRef.current = setTimeout(async () => {
            try {
                const resp = await fetch(`${CONFIG.API_URL}/geocode?latitude=${pos.latitude}&longitude=${pos.longitude}`, {
                    headers: { Authorization: `Bearer ${user.token}` }
                });
                const data = await resp.json();
                const addr = data.address || '';
                geocodeCacheRef.current[key] = addr;
                setCurrentAddress(addr);
            } catch (e) { setCurrentAddress(''); }
        }, isPlaying ? 2000 : 300);
        return () => { if (geocodeTimerRef.current) clearTimeout(geocodeTimerRef.current); };
    }, [currentIndex, positions, isPlaying]);

    const fmtHM = (dt) => {
        const parts = dt.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Zurich' }).split(':');
        return `${parts[0]}h${parts[1]}`;
    };
    const formatTime = (dateStr) => {
        if (!dateStr) return '';
        let d = String(dateStr).replace(' ', 'T');
        if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
        return fmtHM(new Date(d));
    };
    const formatDateTime = (dateStr) => {
        if (!dateStr) return '';
        let d = String(dateStr).replace(' ', 'T');
        if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
        const dt = new Date(d);
        const day = dt.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: 'Europe/Zurich' });
        return `${day} ${fmtHM(dt)}`;
    };

    const applyCustomDateRange = useCallback((start, end) => {
        if (!start || !end) return;
        setPeriodPreset('custom');
        setDateStart(start);
        setDateEnd(end);
        setTimeStart('00:00');
        setTimeEnd('23:59');
    }, []);

    const [apiTrips, setApiTrips] = useState([]);
    const [apiParkings, setApiParkings] = useState([]);
    const [apiTowing, setApiTowing] = useState([]);
    const [listLoading, setListLoading] = useState(false);

    const loadComputedData = useCallback(async () => {
        if (!user?.token || !selectedImei || !dateStart) return;
        setListLoading(true);
        setApiTrips([]); setApiParkings([]); setApiTowing([]);
        try {
            const startD = new Date(dateStart);
            const endD = new Date(dateEnd || dateStart);
            const allTrips = [];
            const allParkings = [];
            const allTowing = [];
            for (let d = new Date(startD); d <= endD; d = new Date(d.getTime() + 86400000)) {
                const dayStr = d.toISOString().slice(0, 10);
                const resp = await fetch(`${CONFIG.API_URL}/replay/computed?imei=${selectedImei}&date=${dayStr}`, {
                    headers: { Authorization: `Bearer ${user.token}` }
                });
                if (checkAuth(resp)) { setListLoading(false); return; }
                if (resp.ok) {
                    const data = await resp.json();
                    if (data.trips) allTrips.push(...data.trips);
                    if (data.parkings) allParkings.push(...data.parkings);
                    if (data.towingEvents) allTowing.push(...data.towingEvents);
                }
            }
            setApiTrips(allTrips);
            setApiParkings(allParkings);
            setApiTowing(allTowing);
        } catch(e) { console.error('Load computed error:', e); }
        setListLoading(false);
    }, [user, selectedImei, dateStart, dateEnd]);

    useEffect(() => {
        if (user?.token && selectedImei && dateStart) {
            loadComputedData();
        }
    }, [selectedImei, dateStart, dateEnd, loadComputedData]);

    const frequentParkings = useMemo(() => {
        if (apiParkings.length === 0) return [];
        const groups = {};
        apiParkings.forEach(p => {
            const lat = p.latitude.toFixed(3);
            const lng = p.longitude.toFixed(3);
            const key = `${lat},${lng}`;
            if (!groups[key]) {
                groups[key] = { latitude: p.latitude, longitude: p.longitude, count: 0, totalDuration: 0, address: p.address };
            }
            groups[key].count++;
            groups[key].totalDuration += (p.duration || 0);
            if (p.address && !groups[key].address) groups[key].address = p.address;
        });
        return Object.values(groups).sort((a, b) => b.count - a.count);
    }, [apiParkings]);

    const tripsTimeline = useMemo(() => {
        const raw = [];
        const sorted = [...apiTrips].sort((a, b) => (a.startTime || '').localeCompare(b.startTime || ''));
        const sortedParkings = [...apiParkings].sort((a, b) => (a.startTime || '').localeCompare(b.startTime || ''));

        let pIdx = 0;
        for (let i = 0; i < sorted.length; i++) {
            while (pIdx < sortedParkings.length && sortedParkings[pIdx].startTime < sorted[i].startTime) {
                raw.push({ type: 'parking', data: sortedParkings[pIdx] });
                pIdx++;
            }
            raw.push({ type: 'trip', data: sorted[i] });
        }
        while (pIdx < sortedParkings.length) {
            raw.push({ type: 'parking', data: sortedParkings[pIdx] });
            pIdx++;
        }

        const merged = [];
        for (let i = 0; i < raw.length; i++) {
            const item = raw[i];
            if (item.type === 'parking' && merged.length > 0) {
                const prev = merged[merged.length - 1];
                if (prev.type === 'parking') {
                    const latSame = Math.abs(prev.data.latitude - item.data.latitude) < 0.001;
                    const lngSame = Math.abs(prev.data.longitude - item.data.longitude) < 0.001;
                    if (latSame && lngSame) {
                        prev.data = { ...prev.data, endTime: item.data.endTime, duration: (prev.data.duration || 0) + (item.data.duration || 0) };
                        continue;
                    }
                }
            }
            merged.push({ ...item });
        }

        const timeline = [];
        for (let i = 0; i < merged.length; i++) {
            const cur = merged[i];
            if (cur.type === 'trip' && cur.data.distance != null && cur.data.distance < 0.1) {
                const prev = i > 0 ? merged[i - 1] : null;
                const next = i < merged.length - 1 ? merged[i + 1] : null;
                if (prev?.type === 'parking' && next?.type === 'parking') {
                    const latSame = Math.abs(prev.data.latitude - next.data.latitude) < 0.002;
                    const lngSame = Math.abs(prev.data.longitude - next.data.longitude) < 0.002;
                    if (latSame && lngSame) {
                        prev.data = { ...prev.data, endTime: next.data.endTime, duration: (prev.data.duration || 0) + (next.data.duration || 0) };
                        i++;
                        continue;
                    }
                }
            }
            timeline.push(cur);
        }
        return timeline;
    }, [apiTrips, apiParkings]);

    const geocodePoint = useCallback(async (lat, lng) => {
        const key = `${lat.toFixed(4)},${lng.toFixed(4)}`;
        if (geocodeCacheRef.current[key]) return geocodeCacheRef.current[key];
        try {
            const resp = await fetch(`${CONFIG.API_URL}/geocode?latitude=${lat}&longitude=${lng}`, { headers: { Authorization: `Bearer ${user?.token}` } });
            const data = await resp.json();
            const addr = data.address || '';
            geocodeCacheRef.current[key] = addr;
            return addr;
        } catch(e) { return ''; }
    }, [user]);

    const showTripOnMap = useCallback(async (trip) => {
        setMapReturnTab('trajets');
        setReplayTab('carte');
        setHideHeader(true);
        setActiveParkingInfo(null);
        setMapViewAddress('');
        if (!user?.token || !selectedImei) return;
        setLoading(true);
        try {
            const fromStr = trip.startTime;
            const toStr = trip.endTime;
            const resp = await fetch(`${CONFIG.API_URL}/positions/imei/${selectedImei}?from=${encodeURIComponent(fromStr)}&to=${encodeURIComponent(toStr)}&limit=15000&address=false`, {
                headers: { Authorization: `Bearer ${user.token}` }
            });
            if (checkAuth(resp)) { setLoading(false); return; }
            if (!resp.ok) { setLoading(false); return; }
            const data = await resp.json();
            const pts = (data.positions || []).reverse();
            if (pts.length > 0) {
                setTimeout(() => {
                    if (!mapInstance.current) return;
                    mapInstance.current.invalidateSize();
                    if (polylineRef.current) { try { mapInstance.current.removeLayer(polylineRef.current); } catch(e) {} polylineRef.current = null; }
                    polylineSegmentsRef.current.forEach(seg => { try { mapInstance.current.removeLayer(seg); } catch(e) {} });
                    polylineSegmentsRef.current = [];
                    startEndMarkersRef.current.forEach(m => { try { mapInstance.current.removeLayer(m); } catch(e) {} });
                    startEndMarkersRef.current = [];
                    clearParkingMarkers(); clearStopMarkers();
                    if (markerRef.current) { try { mapInstance.current.removeLayer(markerRef.current); } catch(e) {} markerRef.current = null; }
                    if (replayBadgeRef.current) { try { mapInstance.current.removeLayer(replayBadgeRef.current); } catch(e) {} replayBadgeRef.current = null; }
                    setPositions(pts);
                    setCurrentIndex(0);
                    setIsPlaying(true);
                    setIsFollowing(true);
                    drawRoute(pts);
                    mapInstance.current.setView([pts[0].latitude, pts[0].longitude], 16, { animate: false });
                    calculateTrips(pts);

                    // Marker Vmax
                    const vmaxPt = pts.reduce((best, p) => (p.speed || 0) > (best.speed || 0) ? p : best, pts[0]);
                    if (vmaxPt && (vmaxPt.speed || 0) > 5) {
                        const vmaxIcon = L.divIcon({
                            html: `<div style="background:#F97316;color:white;border-radius:6px;padding:2px 6px;font-size:11px;font-weight:700;white-space:nowrap;box-shadow:0 2px 6px rgba(0,0,0,0.25);pointer-events:none;">⚡ ${Math.round(vmaxPt.speed)} km/h</div>`,
                            className: '', iconSize: null, iconAnchor: [0, 24]
                        });
                        const vmaxMarker = L.marker([vmaxPt.latitude, vmaxPt.longitude], { icon: vmaxIcon, interactive: false, zIndexOffset: 900 }).addTo(mapInstance.current);
                        startEndMarkersRef.current.push(vmaxMarker);
                    }
                }, 100);
                geocodePoint(pts[0].latitude, pts[0].longitude).then(addr => setMapViewAddress(addr));
            }
        } catch(e) { console.error('Show trip error:', e); }
        setLoading(false);
    }, [user, selectedImei, clearParkingMarkers, clearStopMarkers, calculateTrips, geocodePoint]);

    const showParkingOnMap = useCallback((parking, returnTab) => {
        setMapReturnTab(returnTab || 'parking');
        setReplayTab('carte');
        setHideHeader(true);
        const dur = parking.duration;
        const fmtDur = dur ? (dur >= 3600000 ? `${Math.floor(dur/3600000)}h${String(Math.round((dur%3600000)/60000)).padStart(2,'0')}` : `${Math.round(dur/60000)}min`) : '';
        setActiveParkingInfo({ duration: fmtDur, startTime: parking.startTime, endTime: parking.endTime });
        setMapViewAddress('');
        geocodePoint(parking.latitude, parking.longitude).then(addr => setMapViewAddress(addr));
        if (!mapInstance.current) return;
        setTimeout(() => {
            if (!mapInstance.current) return;
            mapInstance.current.invalidateSize();
            if (polylineRef.current) { try { mapInstance.current.removeLayer(polylineRef.current); } catch(e) {} polylineRef.current = null; }
            polylineSegmentsRef.current.forEach(seg => { try { mapInstance.current.removeLayer(seg); } catch(e) {} });
            polylineSegmentsRef.current = [];
            startEndMarkersRef.current.forEach(m => { try { mapInstance.current.removeLayer(m); } catch(e) {} });
            startEndMarkersRef.current = [];
            clearParkingMarkers(); clearStopMarkers();
            if (markerRef.current) { try { mapInstance.current.removeLayer(markerRef.current); } catch(e) {} markerRef.current = null; }
            if (replayBadgeRef.current) { try { mapInstance.current.removeLayer(replayBadgeRef.current); } catch(e) {} replayBadgeRef.current = null; }
            setPositions([]);
            setCurrentIndex(0);
            setIsPlaying(false);

            const count = parking.count;
            const iconSize = count ? 44 : 36;
            const parkIcon = L.divIcon({
                html: count
                    ? `<div style="position:relative;width:${iconSize}px;height:${iconSize}px;">
                        <div style="width:${iconSize}px;height:${iconSize}px;border-radius:50%;background:#3B82F6;border:3px solid white;box-shadow:0 2px 8px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;">
                            <span style="font-size:18px;font-weight:800;color:white;">P</span>
                        </div>
                        <div style="position:absolute;top:-6px;right:-6px;min-width:20px;height:20px;border-radius:10px;background:#EF4444;border:2px solid white;display:flex;align-items:center;justify-content:center;padding:0 4px;">
                            <span style="font-size:11px;font-weight:700;color:white;">${count}</span>
                        </div>
                    </div>`
                    : `<div style="width:36px;height:36px;border-radius:50%;background:#3B82F6;border:3px solid white;box-shadow:0 2px 8px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;">
                        <span style="font-size:16px;font-weight:800;color:white;">P</span>
                    </div>`,
                className: 'custom-marker', iconSize: [iconSize, iconSize], iconAnchor: [iconSize/2, iconSize/2]
            });
            const dur = parking.duration;
            const durStr = dur ? (dur >= 3600000 ? `${Math.floor(dur/3600000)}h${String(Math.round((dur%3600000)/60000)).padStart(2,'0')}min` : `${Math.round(dur/60000)}min`) : '';
            const fmtT = (ts) => { if (!ts) return ''; let d = String(ts).replace(' ','T'); if (!d.endsWith('Z') && !d.includes('+')) d+='Z'; const dt = new Date(d); const p = dt.toLocaleTimeString('en-GB',{hour:'2-digit',minute:'2-digit',hour12:false,timeZone:'Europe/Zurich'}).split(':'); return `${p[0]}h${p[1]}`; };
            const timeRange = parking.startTime && parking.endTime ? `${fmtT(parking.startTime)} → ${fmtT(parking.endTime)}` : '';

            const labelHtml = `<div style="background:white;border-radius:8px;padding:4px 8px;box-shadow:0 2px 8px rgba(0,0,0,0.18);text-align:center;white-space:nowrap;pointer-events:none;">
                ${durStr ? `<div style="font-size:13px;font-weight:700;color:#1e293b;line-height:1.2;">${durStr}</div>` : ''}
                ${timeRange ? `<div style="font-size:11px;color:#64748b;line-height:1.2;">${timeRange}</div>` : ''}
            </div>`;
            const labelIcon = L.divIcon({ html: labelHtml, className: '', iconSize: null, iconAnchor: [0, iconSize + 8] });
            const labelMarker = L.marker([parking.latitude, parking.longitude], { icon: labelIcon, interactive: false, zIndexOffset: 1000 }).addTo(mapInstance.current);
            startEndMarkersRef.current.push(labelMarker);

            markerRef.current = L.marker([parking.latitude, parking.longitude], { icon: parkIcon }).addTo(mapInstance.current);

            mapInstance.current.setView([parking.latitude, parking.longitude], 17, { animate: true });
        }, 100);
    }, [clearParkingMarkers, clearStopMarkers, geocodePoint]);

    if (!user) {
        return (
            <AuthModal onClose={() => {}} />
        );
    }

    const fmtDuration = (ms) => {
        if (!ms || ms < 0) return '0min';
        const h = Math.floor(ms / 3600000);
        const m = Math.floor((ms % 3600000) / 60000);
        return h > 0 ? `${h}h${m > 0 ? `${String(m).padStart(2, '0')}min` : ''}` : `${m}min`;
    };

    const fmtTripTime = (dateStr) => {
        if (!dateStr) return '';
        let d = String(dateStr).replace(' ', 'T');
        if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
        return fmtHM(new Date(d));
    };

    return (
        <div className="h-full flex flex-col">
            {/* Period selector - hidden when viewing carte */}
            {replayTab !== 'carte' && (
            <div className="bg-white border-b border-gray-200 px-4 py-2.5 flex flex-col gap-2">
                <div className="flex items-center gap-1.5">
                    {[{ key: 'today', label: 'Auj.' }, { key: 'yesterday', label: 'Hier' }, { key: 'daybeforeyesterday', label: 'Av. hier' }].map(p => (
                        <button key={p.key} onClick={() => applyPeriodPreset(p.key)}
                            className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors ${periodPreset === p.key
                                ? 'bg-blue-500 text-white'
                                : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
                            }`}>
                            {p.label}
                        </button>
                    ))}
                    <div className="w-px h-5 bg-gray-200 mx-0.5"></div>
                    <button onClick={() => applyPeriodPreset('custom')}
                        className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors flex items-center gap-1 ${periodPreset === 'custom'
                            ? 'bg-blue-500 text-white'
                            : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
                        }`}>
                        <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <rect x="3" y="4" width="18" height="18" rx="2"></rect>
                            <path d="M16 2v4M8 2v4M3 10h18"></path>
                        </svg>
                        Dates
                    </button>
                </div>
                {showDateRange && (
                    <div className="flex items-center gap-2">
                        <input type="date" max={new Date().toISOString().split('T')[0]} value={dateStart}
                            onChange={(e) => { setDateStart(e.target.value); if (dateEnd && e.target.value) applyCustomDateRange(e.target.value, dateEnd); }}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                        <span className="text-xs text-gray-400 font-medium">&rarr;</span>
                        <input type="date" min={dateStart} max={new Date().toISOString().split('T')[0]} value={dateEnd}
                            onChange={(e) => { setDateEnd(e.target.value); if (dateStart && e.target.value) applyCustomDateRange(dateStart, e.target.value); }}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                    </div>
                )}
            </div>
            )}

            {/* Tabs - hidden when viewing carte */}
            {replayTab !== 'carte' && (
            <div className="bg-white border-b border-gray-200 px-4 py-2">
                <div className="flex bg-gray-100 rounded-lg p-0.5">
                    {[
                        { key: 'trajets', label: 'Trajets', svg: <svg className="w-3.5 h-3.5 inline-block mr-1 -mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l5.447 2.724A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg> },
                        { key: 'parking', label: 'Parking', svg: <svg className="w-3.5 h-3.5 inline-block mr-1 -mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><rect x="4" y="4" width="16" height="16" rx="2"/><path strokeLinecap="round" strokeLinejoin="round" d="M9 16V8h4a2.5 2.5 0 010 5H9"/></svg> }
                    ].map(tab => {
                        const active = replayTab === tab.key;
                        return (
                            <button key={tab.key} onClick={() => { setReplayTab(tab.key); setMapReturnTab(null); }}
                                className={`flex-1 py-2 text-xs font-semibold rounded-md transition-all ${active
                                    ? 'bg-white text-gray-800 shadow-sm'
                                    : 'text-gray-500 hover:text-gray-700'
                                }`}>
                                {tab.svg}
                                {tab.label}
                            </button>
                        );
                    })}
                </div>
            </div>
            )}

            {/* === CARTE TAB === */}
            <div className="flex-1 relative" style={{ display: replayTab === 'carte' ? 'flex' : 'none', flexDirection: 'column' }}>
                {mapReturnTab && (
                <div className="bg-white border-b border-gray-100 px-4 py-3 flex items-center gap-3 shadow-sm">
                    <button onClick={() => { setReplayTab(mapReturnTab); setMapReturnTab(null); setActiveParkingInfo(null); setMapViewAddress(''); setHideHeader(false); setIsFollowing(false); setIsPlaying(false); }}
                        className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100">
                        <svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
                        </svg>
                    </button>
                    <h2 className="text-base font-bold text-gray-800">{selectedDevice?.plate || selectedDevice?.name || 'Véhicule'}</h2>
                    {selectedDevice?.vehicle_model && <span className="text-xs text-gray-400">{selectedDevice.vehicle_model}</span>}
                </div>
                )}
                {positions.length > 0 && (
                <div className="bg-white border-b border-gray-100">
                    <div className="px-4 py-2 flex items-center justify-center gap-6">
                        <div className="flex items-center gap-1.5">
                            <span className="text-sm font-bold text-gray-800">
                                {(() => {
                                    const first = positions[0];
                                    const last = positions[positions.length - 1];
                                    const t1 = new Date((first.server_timestamp || first.timestamp).replace(' ', 'T') + (first.server_timestamp?.includes('Z') ? '' : 'Z'));
                                    const t2 = new Date((last.server_timestamp || last.timestamp).replace(' ', 'T') + (last.server_timestamp?.includes('Z') ? '' : 'Z'));
                                    const ms = t2 - t1;
                                    if (ms < 60000) return `${Math.round(ms / 1000)}s`;
                                    if (ms < 3600000) return `${Math.round(ms / 60000)} min`;
                                    const h = Math.floor(ms / 3600000);
                                    const m = Math.round((ms % 3600000) / 60000);
                                    return `${h}h${m > 0 ? `${String(m).padStart(2, '0')}` : ''}`;
                                })()}
                            </span>
                            <span className="text-xs text-gray-400">durée</span>
                        </div>
                        <div className="w-px h-4 bg-gray-200"></div>
                        <div className="flex items-center gap-1.5">
                            <span className="text-sm font-bold text-gray-800">
                                {(() => {
                                    let d = 0;
                                    for (let i = 1; i < positions.length; i++) {
                                        d += calcDistance(positions[i-1].latitude, positions[i-1].longitude, positions[i].latitude, positions[i].longitude);
                                    }
                                    return d < 1 ? `${Math.round(d * 1000)}m` : `${d.toFixed(1)}km`;
                                })()}
                            </span>
                            <span className="text-xs text-gray-400">distance</span>
                        </div>
                    </div>
                </div>
                )}
                {activeParkingInfo && positions.length === 0 && (
                <div className="bg-white border-b border-gray-100">
                    <div className="px-4 py-2 flex items-center justify-center gap-6">
                        <div className="flex items-center gap-1.5">
                            <span style={{ fontSize: '14px' }}>🅿️</span>
                            <span className="text-sm font-bold text-gray-800">{activeParkingInfo.duration || '-'}</span>
                            <span className="text-xs text-gray-400">durée</span>
                        </div>
                        {activeParkingInfo.startTime && activeParkingInfo.endTime && (
                            <>
                                <div className="w-px h-4 bg-gray-200"></div>
                                <div className="flex items-center gap-1.5">
                                    <span className="text-sm font-bold text-gray-800">{formatTime(activeParkingInfo.startTime)} → {formatTime(activeParkingInfo.endTime)}</span>
                                </div>
                            </>
                        )}
                    </div>
                    {mapViewAddress && <div className="px-4 pb-2 text-xs text-gray-500 truncate text-center flex items-center justify-center"><svg className="w-3 h-3 text-gray-400 mr-1 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path strokeLinecap="round" strokeLinejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>{mapViewAddress}</div>}
                </div>
                )}
                <div className="flex-1 relative">
                    <div ref={mapRef} className="map-container"></div>


                    {/* Top right: Map type/tint + center + reload + overlay */}
                    <MapToolbar mapInstance={mapInstance} tileLayerRef={tileLayerRef} extra={
                        <>
                            {mapReturnTab && !activeParkingInfo && (
                                <button onClick={() => {
                                        setIsFollowing(f => {
                                            const next = !f;
                                            if (next && markerRef.current && mapInstance.current) {
                                                mapInstance.current.setView(markerRef.current.getLatLng(), 16, { animate: true });
                                            }
                                            return next;
                                        });
                                    }}
                                    className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${isFollowing ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}
                                    title="Centrer sur le véhicule">
                                    <svg viewBox="0 0 24 24" className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2}>
                                        <path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"></path>
                                    </svg>
                                </button>
                            )}
                            {!activeParkingInfo && <div ref={replayOverlayRef} className="relative">
                                <button onClick={() => { setShowOverlayMenu(!showOverlayMenu); }}
                                    className={`w-10 h-10 rounded-lg shadow-lg flex items-center justify-center ${showOverlayMenu ? 'bg-blue-500 text-white' : 'bg-white hover:bg-gray-50 text-gray-700'}`}>
                                    <Icons.Eye className="w-5 h-5" />
                                </button>
                                {showOverlayMenu && (
                                    <div className="absolute right-12 top-0 bg-white rounded-xl shadow-xl border border-gray-100 py-2 w-52 map-dropdown z-[1001]">
                                        <div className="px-3 py-1.5 text-xs font-semibold text-gray-400 uppercase">Affichage</div>
                                        <button onClick={toggleParkings}
                                            className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                            <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${showParkings ? 'bg-blue-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                                <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                            </div>
                                            <div className="flex items-center gap-2">
                                                <span style={{ fontSize: '14px' }}>🅿️</span>
                                                <span className="font-medium text-gray-700">Parking</span>
                                            </div>
                                        </button>
                                        <button onClick={toggleStops}
                                            className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                            <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${showStops ? 'bg-orange-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                                <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                            </div>
                                            <div className="flex items-center gap-2">
                                                <span style={{ fontSize: '14px' }}>⏸️</span>
                                                <span className="font-medium text-gray-700">Arrêts</span>
                                            </div>
                                        </button>
                                        <button onClick={toggleDashedTrail}
                                            className="w-full px-3 py-2.5 text-sm text-left flex items-center gap-3 hover:bg-gray-50">
                                            <div className={`w-8 h-5 rounded-full transition-colors flex items-center ${showDashedTrail ? 'bg-green-500 justify-end' : 'bg-gray-300 justify-start'}`}>
                                                <div className="w-4 h-4 bg-white rounded-full shadow mx-0.5"></div>
                                            </div>
                                            <div className="flex items-center gap-2">
                                                <svg width="16" height="16" viewBox="0 0 16 16"><line x1="0" y1="8" x2="16" y2="8" stroke="#22C55E" strokeWidth="2.5" strokeDasharray="3 3"/></svg>
                                                <span className="font-medium text-gray-700">Tracé pointillé</span>
                                            </div>
                                        </button>
                                    </div>
                                )}
                            </div>}
                        </>
                    } />

                    {loading && (
                        <div className="absolute inset-0 flex items-center justify-center z-[999] pointer-events-none">
                            <div className="w-10 h-10 border-[3px] border-blue-500 border-t-transparent rounded-full animate-spin"></div>
                        </div>
                    )}

                    {/* Playback controls */}
                    {positions.length > 0 && (
                        <div className="absolute bottom-0 left-0 right-0 bg-white shadow-t z-[1000]" style={{ boxShadow: '0 -2px 10px rgba(0,0,0,0.1)' }}>
                            {currentAddress && currentAddress !== '...' && (
                                <div className="px-4 py-1.5 flex items-center justify-center border-b border-gray-100">
                                    <svg className="w-3 h-3 text-gray-400 mr-1 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path strokeLinecap="round" strokeLinejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
                                    <span className="text-xs text-gray-500 truncate">{currentAddress}</span>
                                </div>
                            )}
                            <div className="flex items-center gap-3 px-3 py-2">
                                <button onClick={() => setIsPlaying(!isPlaying)}
                                    className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center text-white flex-shrink-0">
                                    {isPlaying ? <Icons.Pause className="w-5 h-5" /> : <Icons.Play className="w-5 h-5" />}
                                </button>
                                <button onClick={() => setPlaybackSpeed(s => { const speeds = [0.2, 0.5, 1, 2]; const idx = speeds.indexOf(s); return speeds[(idx + 1) % speeds.length]; })}
                                    className="px-2 py-1 bg-gray-100 rounded-lg text-xs font-bold text-gray-700 flex-shrink-0 min-w-[36px] text-center">
                                    {playbackSpeed}x
                                </button>
                                <div className="flex-1">
                                    <input type="range" min={0} max={positions.length - 1} value={currentIndex}
                                        onChange={(e) => setCurrentIndex(parseInt(e.target.value))} className="w-full" />
                                </div>
                                <div className="text-sm text-gray-600 min-w-[50px] text-right">
                                    {positions[currentIndex] ? formatTime(positions[currentIndex].server_timestamp || positions[currentIndex].timestamp) : ''}
                                </div>
                            </div>
                        </div>
                    )}
                </div>

            </div>

            {/* === TRAJETS TAB === */}
            {replayTab === 'trajets' && (
                <div className="flex-1 overflow-y-auto bg-gray-50">
                    {tripsTimeline.length > 0 ? (
                        <div>
                            {(() => {
                                let lastDay = null;
                                return tripsTimeline.map((item, idx) => {
                                const ts = item.data.startTime;
                                let dayKey = '';
                                if (ts) {
                                    let d = ts.replace(' ', 'T');
                                    if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
                                    const dt = new Date(d);
                                    dayKey = dt.toLocaleDateString('fr-CH', { timeZone: 'Europe/Zurich', year: 'numeric', month: '2-digit', day: '2-digit' });
                                }
                                const showDayHeader = dayKey && dayKey !== lastDay;
                                if (showDayHeader) lastDay = dayKey;
                                const dayLabel = showDayHeader ? (() => {
                                    let d = ts.replace(' ', 'T');
                                    if (!d.endsWith('Z') && !d.includes('+')) d += 'Z';
                                    const dt = new Date(d);
                                    const str = dt.toLocaleDateString('fr-FR', { timeZone: 'Europe/Zurich', weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
                                    return str.charAt(0).toUpperCase() + str.slice(1);
                                })() : null;
                                return (<React.Fragment key={`frag-${idx}`}>
                                {showDayHeader && (
                                    <div className="sticky top-0 z-10 bg-gray-100 border-b border-gray-200 px-4 py-2">
                                        <span className="text-xs font-bold text-gray-600">{dayLabel}</span>
                                    </div>
                                )}
                                {(() => {
                                if (item.type === 'parking') {
                                    const p = item.data;
                                    return (
                                        <button key={`p-${idx}`} onClick={() => showParkingOnMap(p, 'trajets')}
                                            className="w-full bg-blue-50 px-4 py-2.5 flex items-center gap-3 border-b border-blue-100 hover:bg-blue-100 transition-colors text-left">
                                            <div className="flex flex-col items-center flex-shrink-0">
                                                <span style={{ fontSize: '16px' }}>🅿️</span>
                                            </div>
                                            <div className="flex-1 min-w-0">
                                                <div className="text-xs font-medium text-blue-800 truncate">{p.address || 'Parking'}</div>
                                                <div className="text-[11px] text-blue-600 mt-0.5">
                                                    {fmtTripTime(p.startTime)} → {fmtTripTime(p.endTime)} · {fmtDuration(p.duration)}
                                                </div>
                                            </div>
                                            <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-blue-400 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                                <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
                                            </svg>
                                        </button>
                                    );
                                }
                                const trip = item.data;
                                const duration = (() => {
                                    let s = String(trip.startTime).replace(' ', 'T');
                                    let e = String(trip.endTime).replace(' ', 'T');
                                    if (!s.endsWith('Z') && !s.includes('+')) s += 'Z';
                                    if (!e.endsWith('Z') && !e.includes('+')) e += 'Z';
                                    return new Date(e) - new Date(s);
                                })();
                                return (
                                    <button key={`t-${idx}`} onClick={() => showTripOnMap(trip)}
                                        className="w-full bg-white px-4 py-3 flex items-start gap-3 hover:bg-gray-50 transition-colors text-left border-b border-gray-100">
                                        <div className="flex flex-col items-center mt-0.5 flex-shrink-0">
                                            <div className="w-3 h-3 rounded-full bg-green-500 border-2 border-white shadow"></div>
                                            <div className="w-0.5 flex-1 bg-gray-200 my-0.5" style={{ minHeight: '24px' }}></div>
                                            <div className="w-3 h-3 rounded-full bg-red-500 border-2 border-white shadow"></div>
                                        </div>
                                        <div className="flex-1 min-w-0">
                                            <div className="flex items-center gap-2">
                                                <span className="text-xs font-semibold text-gray-800">{fmtTripTime(trip.startTime)}</span>
                                                <span className="text-xs text-gray-400 truncate">{trip.startAddress || '...'}</span>
                                            </div>
                                            <div className="flex items-center gap-3 my-1.5">
                                                <span className="text-[11px] text-gray-400">{fmtDuration(duration)}</span>
                                                <span className="text-[11px] text-gray-400">{trip.distance < 1 ? `${Math.round(trip.distance * 1000)}m` : `${trip.distance.toFixed(1)}km`}</span>
                                                <span className="text-[11px] text-gray-400">Vmax {Math.round(trip.maxSpeed)} km/h</span>
                                            </div>
                                            <div className="flex items-center gap-2">
                                                <span className="text-xs font-semibold text-gray-800">{fmtTripTime(trip.endTime)}</span>
                                                <span className="text-xs text-gray-400 truncate">{trip.endAddress || '...'}</span>
                                            </div>
                                        </div>
                                        <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-gray-300 mt-3 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                            <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
                                        </svg>
                                    </button>
                                );
                                })()}
                                </React.Fragment>);
                                });
                            })()}
                        </div>
                    ) : (
                        <div className="flex flex-col items-center justify-center py-12 text-gray-400">
                            <svg xmlns="http://www.w3.org/2000/svg" className="w-12 h-12 mb-3 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l5.447 2.724A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
                            </svg>
                            <p className="text-sm">{listLoading ? 'Chargement...' : selectedDevice ? 'Aucun trajet pour cette période' : 'Sélectionnez un véhicule'}</p>
                        </div>
                    )}
                </div>
            )}

            {/* === PARKING TAB === */}
            {replayTab === 'parking' && (
                <div className="flex-1 overflow-y-auto bg-gray-50">
                    {frequentParkings.length > 0 ? (
                        <div className="divide-y divide-gray-100">
                            {frequentParkings.map((p, idx) => (
                                <button key={idx} onClick={() => showParkingOnMap(p, 'parking')}
                                    className="w-full bg-white px-4 py-3 flex items-center gap-3 hover:bg-gray-50 transition-colors text-left">
                                    <div className="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center flex-shrink-0">
                                        <span className="text-lg font-bold text-blue-600">{p.count}</span>
                                    </div>
                                    <div className="flex-1 min-w-0">
                                        <div className="text-sm font-medium text-gray-800 truncate">
                                            {p.address || <span className="text-gray-400 italic">Adresse inconnue</span>}
                                        </div>
                                        <div className="text-xs text-gray-400 mt-0.5">
                                            {p.count} visite{p.count > 1 ? 's' : ''} · Durée totale: {fmtDuration(p.totalDuration)}
                                        </div>
                                    </div>
                                    <svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-gray-400 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                        <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
                                    </svg>
                                </button>
                            ))}
                        </div>
                    ) : (
                        <div className="flex flex-col items-center justify-center py-12 text-gray-400">
                            <span className="text-4xl mb-3">🅿️</span>
                            <p className="text-sm">{listLoading ? 'Chargement...' : selectedDevice ? 'Aucun parking pour cette période' : 'Sélectionnez un véhicule'}</p>
                        </div>
                    )}
                </div>
            )}

            {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
        </div>
    );
};

// ==================== STYLE SUBVIEW ====================
const StyleView = ({ onBack }) => {
    const { markerStyle, changeMarkerStyle, mapType, changeMapType: ctxChangeType, mapTint, changeMapTint: ctxChangeTint } = useAppContext();

    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                <button onClick={onBack} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors">
                    <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                </button>
                <h2 className="text-lg font-bold text-gray-800">Style</h2>
            </div>
            <div className="flex-1 overflow-y-auto">
                <div className="px-4 py-4 space-y-4">
                    {/* Marker Style */}
                    <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                        <div className="px-4 pt-4 pb-2 flex items-center gap-2">
                            <div className="w-8 h-8 rounded-lg bg-blue-100 flex items-center justify-center">
                                <Icons.MapPin className="w-4 h-4 text-blue-600" />
                            </div>
                            <span className="font-semibold text-gray-800 text-sm">Style de marqueur</span>
                        </div>
                        <div className="px-4 pb-4 flex gap-3">
                            <button onClick={() => changeMarkerStyle('standard')}
                                className={`flex-1 relative flex flex-col items-center gap-2 p-3.5 rounded-xl border-2 transition-all ${markerStyle === 'standard' ? 'border-blue-500 bg-blue-50 shadow-sm' : 'border-gray-100 bg-gray-50 hover:border-gray-200'}`}>
                                {markerStyle === 'standard' && <div className="absolute top-2 right-2 w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center"><Icons.Check className="w-3 h-3 text-white" /></div>}
                                <div className="w-11 h-11 rounded-full bg-green-500 flex items-center justify-center shadow-md">
                                    <svg viewBox="0 0 24 24" width="18" height="18" fill="white"><polygon points="12,2 22,22 12,17 2,22"/></svg>
                                </div>
                                <span className={`text-xs font-semibold ${markerStyle === 'standard' ? 'text-blue-600' : 'text-gray-500'}`}>Standard</span>
                            </button>
                            <button onClick={() => changeMarkerStyle('vehicle')}
                                className={`flex-1 relative flex flex-col items-center gap-2 p-3.5 rounded-xl border-2 transition-all ${markerStyle === 'vehicle' ? 'border-blue-500 bg-blue-50 shadow-sm' : 'border-gray-100 bg-gray-50 hover:border-gray-200'}`}>
                                {markerStyle === 'vehicle' && <div className="absolute top-2 right-2 w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center"><Icons.Check className="w-3 h-3 text-white" /></div>}
                                <div className="w-11 h-11 flex items-center justify-center" style={{ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.25))' }}>
                                    <img src="assets/svg/voiture.svg" alt="" style={{ width: '34px' }} />
                                </div>
                                <span className={`text-xs font-semibold ${markerStyle === 'vehicle' ? 'text-blue-600' : 'text-gray-500'}`}>Véhicule</span>
                            </button>
                        </div>
                    </div>

                </div>
            </div>
        </div>
    );
};

// ==================== SUBSCRIPTIONS VIEW ====================
const SubscriptionsView = ({ onBack }) => {
    const { devices } = useAppContext();

    const getPlanLabel = (name) => {
        if (!name) return 'Aucun';
        const n = name.toLowerCase();
        if (n.includes('essai') || n.includes('trial')) return 'Essai 7 jours';
        if (n.includes('pro')) return 'EasyTrack Pro';
        return name;
    };

    const getPlanStyle = (name) => {
        if (!name) return { bg: 'bg-gray-100', text: 'text-gray-500', badge: 'bg-gray-200 text-gray-600' };
        const n = name.toLowerCase();
        if (n.includes('essai') || n.includes('trial')) return { bg: 'bg-amber-50', text: 'text-amber-700', badge: 'bg-amber-100 text-amber-700' };
        return { bg: 'bg-blue-50', text: 'text-blue-700', badge: 'bg-blue-100 text-blue-700' };
    };

    const isExpired = (endDate) => endDate && new Date(endDate) < new Date();
    const daysLeft = (endDate) => {
        if (!endDate) return null;
        const diff = Math.ceil((new Date(endDate) - new Date()) / 86400000);
        return diff > 0 ? diff : 0;
    };

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

    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                <button onClick={onBack} className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                </button>
                <h2 className="text-base font-bold text-gray-800">Mes abonnements</h2>
            </div>

            <div className="flex-1 overflow-y-auto p-4 space-y-3">
                {devices.length === 0 && (
                    <div className="text-center py-12 text-gray-400">
                        <Icons.CreditCard className="w-12 h-12 mx-auto mb-3 text-gray-300" />
                        <p className="text-sm">Aucun abonnement</p>
                    </div>
                )}
                {devices.map(device => {
                    const plan = getPlanLabel(device.subscription_name);
                    const style = getPlanStyle(device.subscription_name);
                    const expired = isExpired(device.subscription_end_date);
                    const remaining = daysLeft(device.subscription_end_date);

                    return (
                        <div key={device.imei} className={`bg-white rounded-2xl shadow-sm overflow-hidden border ${expired ? 'border-red-200' : 'border-gray-100'}`}>
                            <div className="p-4">
                                <div className="flex items-center justify-between mb-3">
                                    <div className="flex items-center gap-2 min-w-0">
                                        <span className="font-semibold text-gray-800 text-sm truncate">{device.plate || device.name}</span>
                                        {device.vehicle_model && <span className="text-xs text-gray-400 truncate">• {device.vehicle_model}</span>}
                                    </div>
                                    <span className={`text-[11px] font-semibold px-2.5 py-1 rounded-full ${expired ? 'bg-red-100 text-red-600' : style.badge}`}>
                                        {expired ? 'Expiré' : plan}
                                    </span>
                                </div>

                                <div className="grid grid-cols-2 gap-3 text-xs">
                                    <div>
                                        <div className="text-gray-400 mb-0.5">Début</div>
                                        <div className="text-gray-700 font-medium">{formatDate(device.subscription_start_date)}</div>
                                    </div>
                                    <div>
                                        <div className="text-gray-400 mb-0.5">Fin</div>
                                        <div className={`font-medium ${expired ? 'text-red-500' : 'text-gray-700'}`}>{formatDate(device.subscription_end_date)}</div>
                                    </div>
                                </div>

                                {!expired && remaining !== null && remaining <= 30 && (
                                    <div className="mt-3 flex items-center gap-2">
                                        <div className="flex-1 h-1.5 bg-gray-100 rounded-full overflow-hidden">
                                            <div className={`h-full rounded-full ${remaining <= 7 ? 'bg-red-400' : remaining <= 14 ? 'bg-amber-400' : 'bg-blue-400'}`}
                                                style={{ width: `${Math.max(100 - (remaining / 30) * 100, 5)}%` }}></div>
                                        </div>
                                        <span className={`text-[11px] font-medium ${remaining <= 7 ? 'text-red-500' : 'text-gray-500'}`}>
                                            {remaining}j restants
                                        </span>
                                    </div>
                                )}
                            </div>
                        </div>
                    );
                })}
            </div>
        </div>
    );
};

// ==================== AFFILIATION VIEW ====================
const AffiliationView = ({ onBack }) => {
    const { user } = useAppContext();
    const [promoData, setPromoData] = useState(null);
    const [canRefer, setCanRefer] = useState(false);
    const [referrals, setReferrals] = useState([]);
    const [loading, setLoading] = useState(true);
    const [customCode, setCustomCode] = useState('');
    const [editingCode, setEditingCode] = useState(false);
    const [saving, setSaving] = useState(false);
    const [copied, setCopied] = useState(false);
    const [balance, setBalance] = useState(0);
    const [enterCode, setEnterCode] = useState(false);
    const [inputCode, setInputCode] = useState('');
    const [applying, setApplying] = useState(false);
    const [applyMsg, setApplyMsg] = useState(null);

    useEffect(() => {
        if (!user?.token) return;
        Promise.all([
            fetch(`${CONFIG.API_URL}/affiliates/my-code`, { headers: { Authorization: `Bearer ${user.token}` } }).then(r => { if (checkAuth(r)) return {}; return r.json(); }),
            fetch(`${CONFIG.API_URL}/affiliates/referrals`, { headers: { Authorization: `Bearer ${user.token}` } }).then(r => { if (checkAuth(r)) return {}; return r.json(); })
        ]).then(([pd, rd]) => {
            setCanRefer(!!pd.can_refer);
            if (pd.promo_code) { setPromoData(pd); setCustomCode(pd.promo_code.code); }
            else { setPromoData(pd); }
            if (rd.referrals) { setReferrals(rd.referrals); setBalance(rd.balance || 0); }
            setLoading(false);
        }).catch(() => setLoading(false));
    }, [user]);

    const saveCode = async () => {
        if (!customCode || customCode.length < 4) return;
        setSaving(true);
        try {
            const r = await fetch(`${CONFIG.API_URL}/affiliates/my-code`, {
                method: 'POST', headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' },
                body: JSON.stringify({ code: customCode })
            });
            const d = await r.json();
            if (d.success) { setEditingCode(false); setPromoData(p => ({ ...p, promo_code: { ...p.promo_code, code: customCode } })); }
            else alert(d.error || 'Erreur');
        } catch(e) { alert('Erreur réseau'); }
        setSaving(false);
    };

    const copyCode = () => {
        const code = promoData?.promo_code?.code;
        if (!code) return;
        navigator.clipboard?.writeText(code).catch(() => {});
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
    };

    const applyPromoCode = async () => {
        const code = inputCode.trim().toUpperCase();
        if (!code || code.length < 4) { setApplyMsg({ type: 'error', text: 'Le code doit contenir au moins 4 caractères.' }); return; }
        setApplying(true);
        setApplyMsg(null);
        try {
            const r = await fetch(`${CONFIG.API_URL}/affiliates/apply-code`, {
                method: 'POST',
                headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' },
                body: JSON.stringify({ code })
            });
            const d = await r.json();
            setApplying(false);
            if (d.success) {
                setApplyMsg({ type: 'success', text: `Votre prochaine activation sera gratuite grâce au code de ${d.referrer_name || 'votre parrain'} !` });
                setInputCode('');
            } else {
                setApplyMsg({ type: 'error', text: d.error || 'Code invalide.' });
            }
        } catch(e) {
            setApplying(false);
            setApplyMsg({ type: 'error', text: 'Erreur réseau.' });
        }
    };

    const fmtDate = (s) => { if (!s) return '-'; return new Date(s.replace(' ','T')+'Z').toLocaleDateString('fr-CH', { day:'2-digit', month:'2-digit', year:'numeric' }); };
    const statusLabel = { pending: 'En attente', confirmed: 'Confirmé', paid: 'Payé', cancelled: 'Annulé' };
    const statusColor = { pending: 'text-amber-600 bg-amber-50', confirmed: 'text-blue-600 bg-blue-50', paid: 'text-green-600 bg-green-50', cancelled: 'text-gray-400 bg-gray-100' };
    const statusIcon = {
        pending: <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><circle cx="12" cy="12" r="10"/><path strokeLinecap="round" d="M12 6v6l4 2"/></svg>,
        confirmed: <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/></svg>,
        paid: <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8V6m0 8v2"/></svg>,
        cancelled: <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
    };

    const rewardPerDevice = promoData?.reward_per_device || 50;

    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                <button onClick={onBack} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors">
                    <svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7"/></svg>
                </button>
                <h2 className="text-lg font-bold text-gray-800">Parrainage</h2>
            </div>

            <div className="flex-1 overflow-y-auto px-4 py-4 space-y-4">
                {loading ? (
                    <div className="flex items-center justify-center py-16"><div className="w-8 h-8 border-[3px] border-green-500 border-t-transparent rounded-full animate-spin"></div></div>
                ) : (
                    <>
                    {canRefer && <>
                    {/* Comment ça marche */}
                    <div className="bg-white rounded-2xl shadow-sm p-4">
                        <h2 className="font-bold text-gray-800 mb-3 text-sm">Comment ça fonctionne</h2>
                        <div className="space-y-3">
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-green-100 flex items-center justify-center flex-shrink-0">
                                    <svg className="w-4 h-4 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"/></svg>
                                </div>
                                <div>
                                    <p className="text-sm font-semibold text-gray-800">Partagez votre code</p>
                                    <p className="text-xs text-gray-500 mt-0.5">Envoyez votre code promo à un ami</p>
                                </div>
                            </div>
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-blue-100 flex items-center justify-center flex-shrink-0">
                                    <svg className="w-4 h-4 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7"/></svg>
                                </div>
                                <div>
                                    <p className="text-sm font-semibold text-gray-800">Votre ami économise</p>
                                    <p className="text-xs text-gray-500 mt-0.5">50% sur sa première activation, ou 1 activation offerte dès 2 devices</p>
                                </div>
                            </div>
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0">
                                    <svg className="w-4 h-4 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8V6m0 8v2m-6-6h.01M18 10h.01"/></svg>
                                </div>
                                <div>
                                    <p className="text-sm font-semibold text-gray-800">Vous gagnez {rewardPerDevice} CHF</p>
                                    <p className="text-xs text-gray-500 mt-0.5">Par abonnement activé par votre filleul</p>
                                </div>
                            </div>
                        </div>
                    </div>

                    {/* Code promo */}
                    <div className="bg-white rounded-2xl shadow-sm p-4">
                        <h2 className="font-bold text-gray-800 mb-3 text-sm">Votre code promo</h2>
                        <div className="space-y-3">
                            <div className="bg-green-50 border-2 border-dashed border-green-300 rounded-xl py-4 px-6 text-center">
                                <div className="text-3xl font-black tracking-widest text-green-700 font-mono">{promoData?.promo_code?.code || '-'}</div>
                                <div className="text-xs text-green-500 mt-1">{promoData?.promo_code?.uses_count || 0} utilisation{(promoData?.promo_code?.uses_count || 0) !== 1 ? 's' : ''}</div>
                            </div>
                            <button onClick={copyCode} className={`w-full py-2.5 rounded-xl text-sm font-semibold transition-all flex items-center justify-center gap-1.5 ${copied ? 'bg-green-500 text-white' : 'bg-gray-100 text-gray-700'}`}>
                                {copied ? (
                                    <><svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/></svg> Copié !</>
                                ) : (
                                    <><svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg> Copier le code</>
                                )}
                            </button>
                        </div>
                    </div>
                    </>}

                    {/* J'ai un code promo (masqué si parrain) */}
                    {!canRefer && <div className="bg-white rounded-2xl shadow-sm p-4">
                        <h2 className="font-bold text-gray-800 mb-3 text-sm">J'ai un code promo</h2>
                        <p className="text-xs text-gray-500 mb-3 leading-relaxed">
                            Un ami vous a donné un code ? Entrez-le pour bénéficier de <span className="font-semibold text-blue-600">{promoData?.free_activations || 1} activation{(promoData?.free_activations || 1) > 1 ? 's' : ''} gratuite{(promoData?.free_activations || 1) > 1 ? 's' : ''}</span> sur votre prochaine commande, ou <span className="font-semibold text-blue-600">50% sur votre première activation</span>.
                        </p>
                        <input
                            value={inputCode} onChange={e => setInputCode(e.target.value.toUpperCase())}
                            placeholder="Ex: JEAN001"
                            className="w-full text-center text-lg font-bold tracking-widest border-2 border-gray-200 rounded-xl px-3 py-3 outline-none focus:border-blue-400 focus:ring-2 focus:ring-blue-100 uppercase font-mono mb-3"
                        />
                        <button onClick={applyPromoCode} disabled={applying} className="w-full py-2.5 rounded-xl bg-blue-500 text-white text-sm font-semibold disabled:opacity-50 flex items-center justify-center gap-1.5">
                            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A2 2 0 013 12V7a4 4 0 014-4z"/></svg>
                            {applying ? '...' : 'Appliquer mon code'}
                        </button>
                        {applyMsg && (
                            <div className={`mt-3 p-3 rounded-xl text-xs font-medium ${applyMsg.type === 'success' ? 'bg-green-50 text-green-600 border border-green-200' : 'bg-red-50 text-red-600 border border-red-200'}`}>
                                {applyMsg.text}
                            </div>
                        )}
                    </div>}

                    {canRefer && <div className="bg-white rounded-2xl shadow-sm p-4">
                        <div className="flex items-center justify-between mb-3">
                            <h2 className="font-bold text-gray-800 text-sm">Mes parrainages</h2>
                            {referrals.length > 0 && (
                                <span className="text-xs font-semibold text-green-600 bg-green-50 px-2 py-0.5 rounded-full">{referrals.length}</span>
                            )}
                        </div>
                        {referrals.length > 0 ? (
                            <div className="space-y-0 divide-y divide-gray-100">
                                {referrals.map(r => (
                                    <div key={r.id} className="flex items-center justify-between py-3">
                                        <div className="flex items-center gap-3 min-w-0">
                                            <div className="w-9 h-9 rounded-full bg-gray-100 flex items-center justify-center flex-shrink-0">
                                                <svg className="w-4.5 h-4.5 text-gray-500" 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>
                                            </div>
                                            <div className="min-w-0">
                                                <div className="text-sm font-medium text-gray-800 truncate">{r.referred_email || '-'}</div>
                                                <div className="text-xs text-gray-400 mt-0.5">{fmtDate(r.created_at)}</div>
                                            </div>
                                        </div>
                                        <div className="flex flex-col items-end gap-1 flex-shrink-0 ml-3">
                                            {r.reward_amount > 0 && <div className="text-sm font-bold text-green-600">+{r.reward_amount} CHF</div>}
                                            <span className={`inline-flex items-center gap-1 text-xs font-semibold px-2 py-0.5 rounded-full ${statusColor[r.reward_status] || 'text-gray-500 bg-gray-100'}`}>
                                                {statusIcon[r.reward_status]}
                                                {statusLabel[r.reward_status] || r.reward_status}
                                            </span>
                                        </div>
                                    </div>
                                ))}
                            </div>
                        ) : (
                            <div className="flex flex-col items-center py-6 text-gray-400">
                                <svg className="w-10 h-10 text-gray-300 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}><path strokeLinecap="round" strokeLinejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
                                <p className="text-sm">Aucun parrainage pour le moment</p>
                                <p className="text-xs text-gray-300 mt-1">Partagez votre code pour commencer</p>
                            </div>
                        )}
                    </div>}
                    </>
                )}
            </div>
        </div>
    );
};

// ==================== MY VEHICLES VIEW ====================
const VEHICLE_TYPES = ['Voiture', 'Limousine', 'Minibus', 'Pickup', 'SUV', 'Moto', 'Scooter', 'Camionnette', 'Construction', 'Camion', 'Ambulance'];

const MyVehiclesView = ({ onBack }) => {
    const { user, devices, setDevices, loadDevices } = useAppContext();
    const [editingDevice, setEditingDevice] = useState(null);
    const [editName, setEditName] = useState('');
    const [editPlate, setEditPlate] = useState('');
    const [editModel, setEditModel] = useState('');
    const [editType, setEditType] = useState('Voiture');
    const [saving, setSaving] = useState(false);
    const [saveSuccess, setSaveSuccess] = useState(null);

    const startEdit = (device) => {
        setEditingDevice(device);
        setEditName(device.name || '');
        setEditPlate(device.plate || device.vehicle_plate || '');
        setEditModel(device.vehicle_model || '');
        setEditType(device.vehicle_type || 'Voiture');
        setSaveSuccess(null);
    };

    const saveDevice = async () => {
        if (!editingDevice || !user?.token) return;
        setSaving(true);
        setSaveSuccess(null);
        try {
            const resp = await fetch(`${CONFIG.API_URL}/devices/${editingDevice.id}`, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${user.token}` },
                body: JSON.stringify({
                    name: editName,
                    vehicle_plate: editPlate,
                    vehicle_model: editModel,
                    vehicle_type: editType
                })
            });
            if (checkAuth(resp)) { setSaving(false); return; }
            const data = await resp.json();
            if (resp.ok && data.success) {
                setSaveSuccess(true);
                await loadDevices();
                setTimeout(() => setEditingDevice(null), 800);
            } else {
                setSaveSuccess(false);
            }
        } catch (e) {
            console.error('Save error:', e);
            setSaveSuccess(false);
        }
        setSaving(false);
    };

    if (editingDevice) {
        return (
            <div className="h-full flex flex-col bg-gray-50">
                <div className="bg-white border-b border-gray-100 px-4 py-3 flex items-center gap-3 shadow-sm">
                    <button onClick={() => setEditingDevice(null)} className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100">
                        <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                    </button>
                    <h2 className="text-base font-bold text-gray-800">Modifier le véhicule</h2>
                </div>

                <div className="flex-1 overflow-y-auto p-4 space-y-5">
                    {/* Preview */}
                    <div className="bg-white rounded-2xl shadow-sm p-5 flex flex-col items-center">
                        <div className="w-20 h-20 mb-3 flex items-center justify-center">
                            <img src={`assets/svg/${(VEHICLE_SVG_MAP[editType] || VEHICLE_SVG_MAP['Voiture']).src.replace('assets/svg/', '')}`} className="max-w-full max-h-full" style={{ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))' }} />
                        </div>
                        <div className="text-sm font-bold text-gray-800">{editPlate || editName || editingDevice.imei.slice(-6)}</div>
                        <div className="text-xs text-gray-400">{editModel || editType}</div>
                    </div>

                    {/* Fields */}
                    <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                        <div className="p-4 space-y-4">
                            <div>
                                <label className="text-xs font-semibold text-gray-500 uppercase mb-1.5 block">Plaque</label>
                                <input type="text" value={editPlate} onChange={e => setEditPlate(e.target.value)}
                                    className="w-full px-3 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
                                    placeholder="ex: GE 123456" />
                            </div>
                            <div>
                                <label className="text-xs font-semibold text-gray-500 uppercase mb-1.5 block">Nom / Label</label>
                                <input type="text" value={editName} onChange={e => setEditName(e.target.value)}
                                    className="w-full px-3 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
                                    placeholder="ex: Ma voiture" />
                            </div>
                            <div>
                                <label className="text-xs font-semibold text-gray-500 uppercase mb-1.5 block">Modèle</label>
                                <input type="text" value={editModel} onChange={e => setEditModel(e.target.value)}
                                    className="w-full px-3 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
                                    placeholder="ex: Tesla Model 3" />
                            </div>
                        </div>
                    </div>

                    {/* Vehicle Type Picker */}
                    <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                        <div className="px-4 pt-4 pb-2">
                            <label className="text-xs font-semibold text-gray-500 uppercase">Type de véhicule</label>
                        </div>
                        <div className="px-3 pb-3 grid grid-cols-4 gap-2">
                            {VEHICLE_TYPES.map(type => {
                                const info = VEHICLE_SVG_MAP[type] || VEHICLE_SVG_MAP['Voiture'];
                                const active = editType === type;
                                return (
                                    <button key={type} onClick={() => setEditType(type)}
                                        className={`flex flex-col items-center gap-1.5 p-2.5 rounded-xl transition-all ${active ? 'bg-blue-50 ring-2 ring-blue-400' : 'hover:bg-gray-50 border border-gray-100'}`}>
                                        <div className="w-10 h-10 flex items-center justify-center">
                                            <img src={info.src} className="max-w-full max-h-full" style={{ filter: active ? 'none' : 'grayscale(0.5) opacity(0.7)' }} />
                                        </div>
                                        <span className={`text-[10px] font-medium leading-tight text-center ${active ? 'text-blue-600' : 'text-gray-500'}`}>{type}</span>
                                    </button>
                                );
                            })}
                        </div>
                    </div>

                    {/* IMEI info */}
                    <div className="bg-gray-100 rounded-xl px-4 py-3">
                        <div className="text-xs text-gray-400">IMEI</div>
                        <div className="text-sm font-mono text-gray-600">{editingDevice.imei}</div>
                    </div>

                    {/* Save */}
                    <button onClick={saveDevice} disabled={saving}
                        className={`w-full py-3 rounded-xl font-semibold text-white transition-all ${saving ? 'bg-gray-400' : saveSuccess === true ? 'bg-green-500' : 'bg-blue-500 hover:bg-blue-600 active:scale-[0.98]'}`}>
                        {saving ? 'Enregistrement...' : saveSuccess === true ? '✓ Enregistré' : 'Enregistrer'}
                    </button>
                    {saveSuccess === false && (
                        <div className="text-center text-sm text-red-500">Erreur lors de la sauvegarde</div>
                    )}
                </div>
            </div>
        );
    }

    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-100 px-4 py-3 flex items-center gap-3 shadow-sm">
                <button onClick={onBack} className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100">
                    <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                </button>
                <h2 className="text-base font-bold text-gray-800">Mes véhicules</h2>
            </div>

            <div className="flex-1 overflow-y-auto p-4 space-y-3">
                {devices.length === 0 && (
                    <div className="text-center py-12 text-gray-400">
                        <Icons.Car className="w-12 h-12 mx-auto mb-3 text-gray-300" />
                        <p className="text-sm">Aucun véhicule</p>
                    </div>
                )}
                {devices.map(device => {
                    const status = getDeviceStatus(device.speed || 0, device.timestamp || device.last_seen_at, device.is_active, device.ignition, device.movement);
                    const info = VEHICLE_SVG_MAP[device.vehicle_type] || VEHICLE_SVG_MAP['Voiture'];
                    return (
                        <button key={device.imei} onClick={() => startEdit(device)}
                            className="w-full bg-white rounded-2xl shadow-sm p-4 flex items-center gap-4 hover:bg-gray-50 transition-colors text-left active:scale-[0.98]">
                            <div className="w-14 h-14 rounded-xl bg-gray-50 flex items-center justify-center flex-shrink-0 border border-gray-100">
                                <img src={info.src} className="max-w-[80%] max-h-[80%]" style={{ filter: 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))' }} />
                            </div>
                            <div className="flex-1 min-w-0">
                                <div className="flex items-center gap-2">
                                    <span className="font-semibold text-gray-800 text-sm truncate">{device.plate || device.name || device.imei.slice(-6)}</span>
                                    <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: status.colors.main }}></span>
                                </div>
                                {device.vehicle_model && <div className="text-xs text-gray-400 truncate mt-0.5">{device.vehicle_model}</div>}
                                <div className="text-[11px] text-gray-300 mt-0.5 font-mono">{device.imei}</div>
                            </div>
                            <Icons.ChevronRight className="w-4 h-4 text-gray-300 flex-shrink-0" />
                        </button>
                    );
                })}
            </div>
        </div>
    );
};

// ==================== CHAT VIEW ====================
const ChatView = ({ onBack }) => {
    const { user } = useAppContext();
    const [conversations, setConversations] = useState([]);
    const [activeConv, setActiveConv] = useState(null);
    const [messages, setMessages] = useState([]);
    const [newMessage, setNewMessage] = useState('');
    const [sending, setSending] = useState(false);
    const [loading, setLoading] = useState(true);
    const [showNew, setShowNew] = useState(false);
    const [newSubject, setNewSubject] = useState('');
    const messagesEndRef = useRef(null);

    const req = useCallback(async (url, opts = {}) => {
        const r = await fetch(`${CONFIG.API_URL}${url}`, { ...opts, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}`, ...opts.headers } });
        if (checkAuth(r)) return { success: false, expired: true };
        return r.json();
    }, [user]);

    const loadConversations = useCallback(async () => {
        setLoading(true);
        const data = await req('/chat/conversations');
        setConversations(data.conversations || []);
        setLoading(false);
    }, [req]);

    const openConversation = useCallback(async (conv) => {
        setActiveConv(conv);
        const data = await req(`/chat/conversations/${conv.id}/messages`);
        setMessages(data.messages || []);
        setTimeout(() => messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100);
    }, [req]);

    const sendMessage = useCallback(async () => {
        if (!newMessage.trim() || sending) return;
        setSending(true);
        if (activeConv) {
            await req(`/chat/conversations/${activeConv.id}/messages`, { method: 'POST', body: JSON.stringify({ message: newMessage }) });
            const data = await req(`/chat/conversations/${activeConv.id}/messages`);
            setMessages(data.messages || []);
        } else {
            const data = await req('/chat/conversations', { method: 'POST', body: JSON.stringify({ subject: newSubject || 'Support', message: newMessage }) });
            if (data.conversation_id) {
                setShowNew(false);
                await loadConversations();
                const convData = await req(`/chat/conversations/${data.conversation_id}/messages`);
                setActiveConv({ id: data.conversation_id });
                setMessages(convData.messages || []);
            }
        }
        setNewMessage('');
        setSending(false);
        setTimeout(() => messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100);
    }, [newMessage, sending, activeConv, newSubject, req, loadConversations]);

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

    const fmtDate = (d) => {
        if (!d) return '';
        const dt = new Date(d + (d.includes('Z') || d.includes('+') ? '' : 'Z'));
        const now = new Date();
        const diff = now - dt;
        if (diff < 60000) return "À l'instant";
        if (diff < 3600000) return `${Math.floor(diff / 60000)}min`;
        if (diff < 86400000) { const p = dt.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Zurich' }).split(':'); return `${p[0]}h${p[1]}`; }
        return dt.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', timeZone: 'Europe/Zurich' });
    };

    // New conversation form
    if (showNew && !activeConv) {
        return (
            <div className="h-full flex flex-col bg-gray-50">
                <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                    <button onClick={() => setShowNew(false)} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200">
                        <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                    </button>
                    <h2 className="text-base font-bold text-gray-800">Nouvelle conversation</h2>
                </div>
                <div className="flex-1 px-4 py-4 space-y-3">
                    <input value={newSubject} onChange={e => setNewSubject(e.target.value)} placeholder="Sujet (optionnel)"
                        className="w-full px-4 py-3 rounded-xl border border-gray-200 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none" />
                    <textarea value={newMessage} onChange={e => setNewMessage(e.target.value)} placeholder="Décrivez votre question ou problème..."
                        className="w-full px-4 py-3 rounded-xl border border-gray-200 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none resize-none" rows={6} />
                </div>
                <div className="px-4 py-3 bg-white border-t border-gray-100">
                    <button onClick={sendMessage} disabled={!newMessage.trim() || sending}
                        className="w-full py-3 rounded-xl bg-blue-600 text-white font-semibold text-sm disabled:opacity-50 hover:bg-blue-700 transition-colors">
                        {sending ? 'Envoi...' : 'Envoyer'}
                    </button>
                </div>
            </div>
        );
    }

    // Active conversation messages
    if (activeConv) {
        return (
            <div className="h-full flex flex-col bg-gray-50">
                <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                    <button onClick={() => { setActiveConv(null); loadConversations(); }} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200">
                        <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                    </button>
                    <h2 className="text-base font-bold text-gray-800 truncate">{activeConv.subject || 'Support'}</h2>
                </div>
                <div className="flex-1 overflow-y-auto px-4 py-3 space-y-3">
                    {messages.map(m => (
                        <div key={m.id} className={`flex ${m.sender_type === 'user' ? 'justify-end' : 'justify-start'}`}>
                            <div className={`max-w-[80%] rounded-2xl px-4 py-2.5 ${m.sender_type === 'user'
                                ? 'bg-blue-600 text-white rounded-br-md'
                                : 'bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-md'}`}>
                                {m.sender_type === 'admin' && <div className="text-xs font-semibold text-blue-600 mb-1">{m.sender_name}</div>}
                                <p className="text-sm whitespace-pre-wrap">{m.message}</p>
                                <div className={`text-[10px] mt-1 ${m.sender_type === 'user' ? 'text-blue-200' : 'text-gray-400'}`}>{fmtDate(m.created_at)}</div>
                            </div>
                        </div>
                    ))}
                    <div ref={messagesEndRef} />
                </div>
                <div className="px-4 py-3 bg-white border-t border-gray-100 flex gap-2">
                    <input value={newMessage} onChange={e => setNewMessage(e.target.value)}
                        onKeyDown={e => e.key === 'Enter' && !e.shiftKey && sendMessage()}
                        placeholder="Votre message..." className="flex-1 px-4 py-2.5 rounded-xl border border-gray-200 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none" />
                    <button onClick={sendMessage} disabled={!newMessage.trim() || sending}
                        className="w-10 h-10 rounded-xl bg-blue-600 text-white flex items-center justify-center disabled:opacity-50 hover:bg-blue-700 transition-colors flex-shrink-0">
                        <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M12 19V5m0 0l-7 7m7-7l7 7" style={{ transform: 'rotate(45deg)', transformOrigin: 'center' }} />
                        </svg>
                    </button>
                </div>
            </div>
        );
    }

    // Conversations list
    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                <button onClick={onBack} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200">
                    <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                </button>
                <h2 className="text-base font-bold text-gray-800 flex-1">Aide & Support</h2>
                <button onClick={() => setShowNew(true)} className="w-9 h-9 rounded-xl bg-blue-600 flex items-center justify-center hover:bg-blue-700">
                    <svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
                    </svg>
                </button>
            </div>
            <div className="flex-1 overflow-y-auto">
                {loading ? (
                    <div className="flex items-center justify-center py-12"><div className="w-8 h-8 border-[3px] border-blue-500 border-t-transparent rounded-full animate-spin"></div></div>
                ) : conversations.length === 0 ? (
                    <div className="flex flex-col items-center justify-center py-12 text-gray-400 px-6">
                        <svg className="w-16 h-16 mb-4 text-gray-200" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
                        </svg>
                        <p className="text-sm font-medium text-gray-500 mb-1">Aucune conversation</p>
                        <p className="text-xs text-gray-400 text-center mb-4">Posez vos questions à notre équipe support</p>
                        <button onClick={() => setShowNew(true)} className="px-6 py-2.5 rounded-xl bg-blue-600 text-white text-sm font-semibold hover:bg-blue-700 transition-colors">
                            Démarrer une conversation
                        </button>
                    </div>
                ) : (
                    <div className="divide-y divide-gray-100">
                        {conversations.map(c => (
                            <button key={c.id} onClick={() => openConversation(c)}
                                className="w-full px-4 py-3.5 flex items-center gap-3 hover:bg-gray-50 transition-colors text-left">
                                <div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${c.status === 'open' ? 'bg-blue-100' : 'bg-gray-100'}`}>
                                    <svg className={`w-5 h-5 ${c.status === 'open' ? 'text-blue-600' : 'text-gray-400'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                        <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
                                    </svg>
                                </div>
                                <div className="flex-1 min-w-0">
                                    <div className="flex items-center gap-2">
                                        <span className="text-sm font-semibold text-gray-800 truncate">{c.subject || 'Support'}</span>
                                        {c.unread_user > 0 && <span className="w-5 h-5 rounded-full bg-blue-600 text-white text-[10px] font-bold flex items-center justify-center flex-shrink-0">{c.unread_user}</span>}
                                    </div>
                                    <p className="text-xs text-gray-500 truncate mt-0.5">{c.last_message || ''}</p>
                                </div>
                                <div className="text-[10px] text-gray-400 flex-shrink-0">{fmtDate(c.last_message_at || c.created_at)}</div>
                            </button>
                        ))}
                    </div>
                )}
            </div>
        </div>
    );
};

// ==================== ACCOUNT VIEW ====================
// ==================== NOTIFICATIONS VIEW ====================
const NotificationsView = ({ onBack }) => {
    const { user, registerPushToken } = useAppContext();
    const [loading, setLoading] = useState(true);
    const [saving, setSaving] = useState(false);
    const [hasPushToken, setHasPushToken] = useState(false);
    const [prefs, setPrefs] = useState({
        push_enabled: 0, alert_geofence_enter: 1, alert_geofence_exit: 1,
        alert_speed: 1, alert_battery: 1, alert_offline: 1, alert_towing: 1,
        speed_threshold: 120
    });

    useEffect(() => {
        if (!user?.token) return;
        fetch(`${CONFIG.API_URL}/auth/notification-preferences`, {
            headers: { Authorization: `Bearer ${user.token}` }
        })
        .then(r => { if (checkAuth(r)) return null; return r.json(); })
        .then(data => {
            if (!data) return;
            if (data.success) {
                setPrefs(data.preferences);
                setHasPushToken(data.has_push_token);
            }
        })
        .catch(() => {})
        .finally(() => setLoading(false));
    }, [user?.token]);

    const toggle = async (field) => {
        const newVal = prefs[field] ? 0 : 1;
        if (field === 'push_enabled' && newVal === 1) {
            try {
                const iframe = document.createElement('iframe');
                iframe.style.display = 'none';
                iframe.src = 'registerpush://';
                document.body.appendChild(iframe);
                setTimeout(() => iframe.remove(), 500);
            } catch (e) {}
            setTimeout(() => registerPushToken(), 3000);
            setTimeout(() => registerPushToken(), 8000);
            setTimeout(() => registerPushToken(), 15000);
        }
        const updated = { ...prefs, [field]: newVal };
        setPrefs(updated);
        setSaving(true);
        await fetch(`${CONFIG.API_URL}/auth/notification-preferences`, {
            method: 'PUT',
            headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}` },
            body: JSON.stringify({ [field]: newVal })
        }).catch(() => {});
        setSaving(false);
    };

    const updateThreshold = async (val) => {
        const v = parseInt(val) || 120;
        setPrefs(p => ({ ...p, speed_threshold: v }));
        await fetch(`${CONFIG.API_URL}/auth/notification-preferences`, {
            method: 'PUT',
            headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${user.token}` },
            body: JSON.stringify({ speed_threshold: v })
        }).catch(() => {});
    };

    const ToggleRow = ({ label, subtitle, field, disabled }) => (
        <div className="flex items-center justify-between px-4 py-3.5">
            <div className="flex-1 min-w-0">
                <div className="text-sm font-medium text-gray-800">{label}</div>
                {subtitle && <div className="text-xs text-gray-400 mt-0.5">{subtitle}</div>}
            </div>
            <button
                onClick={() => !disabled && toggle(field)}
                disabled={disabled}
                className="relative flex-shrink-0"
                style={{
                    width: '44px', height: '26px', borderRadius: '13px',
                    background: disabled ? '#E5E7EB' : (prefs[field] ? '#3B82F6' : '#D1D5DB'),
                    transition: 'background 0.2s', border: 'none', cursor: disabled ? 'not-allowed' : 'pointer',
                    opacity: disabled ? 0.5 : 1
                }}
            >
                <div style={{
                    width: '22px', height: '22px', borderRadius: '11px', background: 'white',
                    position: 'absolute', top: '2px',
                    left: prefs[field] ? '20px' : '2px',
                    transition: 'left 0.2s',
                    boxShadow: '0 1px 3px rgba(0,0,0,0.2)'
                }} />
            </button>
        </div>
    );

    return (
        <div className="h-full flex flex-col bg-gray-50">
            <div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center gap-3">
                <button onClick={onBack} className="w-9 h-9 rounded-xl bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors">
                    <Icons.ChevronLeft className="w-5 h-5 text-gray-600" />
                </button>
                <h2 className="text-lg font-bold text-gray-800">Alertes & Notifications</h2>
            </div>
            <div className="flex-1 overflow-y-auto">
                {loading ? (
                    <div className="flex items-center justify-center h-32">
                        <div className="spinner" style={{ width: '2rem', height: '2rem' }}></div>
                    </div>
                ) : (
                    <div className="px-4 py-4 space-y-4">
                        {/* Master toggle */}
                        <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                            <div className="px-4 pt-3.5 pb-1">
                                <span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">Général</span>
                            </div>
                            <ToggleRow label="Notifications push" subtitle={hasPushToken ? 'App iOS connectée' : 'Installez l\'app iOS pour recevoir les push'} field="push_enabled" disabled={!hasPushToken} />
                        </div>

                        {/* Alert types */}
                        <div className="bg-white rounded-2xl shadow-sm overflow-hidden" style={{ opacity: prefs.push_enabled ? 1 : 0.5, pointerEvents: prefs.push_enabled ? 'auto' : 'none' }}>
                            <div className="px-4 pt-3.5 pb-1">
                                <span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">Types d'alertes</span>
                            </div>
                            <ToggleRow label="Entrée de zone" subtitle="Notification quand un véhicule entre dans une geofence" field="alert_geofence_enter" />
                            <div className="h-px bg-gray-100 mx-4"></div>
                            <ToggleRow label="Sortie de zone" subtitle="Notification quand un véhicule quitte une geofence" field="alert_geofence_exit" />
                            <div className="h-px bg-gray-100 mx-4"></div>
                            <ToggleRow label="Excès de vitesse" subtitle={`Seuil : ${prefs.speed_threshold} km/h`} field="alert_speed" />
                            {prefs.alert_speed ? (
                                <div className="px-4 pb-3">
                                    <div className="flex items-center gap-3">
                                        <span className="text-xs text-gray-400 w-12">50</span>
                                        <input type="range" min="50" max="200" step="10" value={prefs.speed_threshold}
                                            onChange={(e) => setPrefs(p => ({ ...p, speed_threshold: parseInt(e.target.value) }))}
                                            onMouseUp={(e) => updateThreshold(e.target.value)}
                                            onTouchEnd={(e) => updateThreshold(e.target.value)}
                                            className="flex-1" style={{ accentColor: '#3B82F6' }}
                                        />
                                        <span className="text-xs text-gray-400 w-12 text-right">200</span>
                                    </div>
                                </div>
                            ) : null}
                            <div className="h-px bg-gray-100 mx-4"></div>
                            <ToggleRow label="Batterie faible" subtitle="Alerte si la batterie du tracker est faible" field="alert_battery" />
                            <div className="h-px bg-gray-100 mx-4"></div>
                            <ToggleRow label="Véhicule hors ligne" subtitle="Alerte si un tracker ne communique plus" field="alert_offline" />
                        </div>

                        {!hasPushToken && (
                            <div className="bg-amber-50 border border-amber-200 rounded-2xl p-4">
                                <p className="text-sm text-amber-700 font-medium">App iOS requise</p>
                                <p className="text-xs text-amber-600 mt-1">Pour recevoir les notifications push, installez l'app EasyTrack sur votre iPhone et connectez-vous.</p>
                            </div>
                        )}
                    </div>
                )}
            </div>
        </div>
    );
};

const AccountView = () => {
    const { user, logout, devices } = useAppContext();
    const [showAuthModal, setShowAuthModal] = useState(false);
    const [showStyleView, setShowStyleView] = useState(false);
    const [showSubscriptions, setShowSubscriptions] = useState(false);
    const [showAffiliation, setShowAffiliation] = useState(false);
    const [showMyVehicles, setShowMyVehicles] = useState(false);
    const [showChat, setShowChat] = useState(false);
    const [showNotifications, setShowNotifications] = useState(false);
    const [canRefer, setCanRefer] = useState(false);

    useEffect(() => { setShowAuthModal(!user); }, [user]);

    useEffect(() => {
        if (!user?.token) return;
        fetch(`${CONFIG.API_URL}/affiliates/my-code`, { headers: { Authorization: `Bearer ${user.token}` } })
            .then(r => { if (checkAuth(r)) return null; return r.json(); })
            .then(d => { if (d) setCanRefer(!!d.can_refer); })
            .catch(() => {});
    }, [user]);

    const initials = useMemo(() => {
        if (!user) return '?';
        if (user.name) return user.name.split(' ').map(w => w[0]).join('').toUpperCase().slice(0, 2);
        if (user.email) return user.email[0].toUpperCase();
        return '?';
    }, [user]);

    const onlineCount = useMemo(() => {
        return devices.filter(d => {
            const s = getDeviceStatus(d.speed || 0, d.timestamp || d.last_seen_at, d.is_active, d.ignition, d.movement);
            return s.status === 'moving' || s.status === 'stopped' || s.status === 'idling';
        }).length;
    }, [devices]);

    if (!user) {
        return (
            <AuthModal onClose={() => {}} />
        );
    }

    if (showStyleView) return <StyleView onBack={() => setShowStyleView(false)} />;
    if (showSubscriptions) return <SubscriptionsView onBack={() => setShowSubscriptions(false)} />;
    if (showAffiliation) return <AffiliationView onBack={() => setShowAffiliation(false)} />;
    if (showMyVehicles) return <MyVehiclesView onBack={() => setShowMyVehicles(false)} />;
    if (showChat) return <ChatView onBack={() => setShowChat(false)} />;
    if (showNotifications) return <NotificationsView onBack={() => setShowNotifications(false)} />;

    return (
        <div className="h-full flex flex-col bg-gray-50">
            {/* Profile Header */}
            <div className="bg-gradient-to-r from-blue-600 to-indigo-600 px-4 py-3.5 flex items-center gap-3">
                <div className="flex-1 min-w-0">
                    <h2 className="text-sm font-bold text-white truncate">{user.name || 'Client'}</h2>
                    <p className="text-blue-200 text-xs truncate">{user.email}</p>
                </div>
                {user.user_code && (
                    <span className="text-xs font-mono font-semibold text-white/60 bg-white/10 px-2.5 py-1 rounded-lg flex-shrink-0">#{user.user_code}</span>
                )}
            </div>

            {/* Content */}
            <div className="flex-1 overflow-y-auto">
                <div className="px-4 py-4 space-y-4">

                    {/* Vehicles, Subscriptions, Style */}
                    <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                        <AccountMenuItem icon={<Icons.Car className="w-5 h-5" />} iconBg="bg-indigo-100" iconColor="text-indigo-600" label="Mes véhicules" subtitle={`${devices.length} véhicule${devices.length > 1 ? 's' : ''} · Nom, icône`} onClick={() => setShowMyVehicles(true)} />
                        <div className="h-px bg-gray-100 mx-4"></div>
                        <AccountMenuItem icon={<Icons.CreditCard className="w-5 h-5" />} iconBg="bg-blue-100" iconColor="text-blue-600" label="Mes trackers" subtitle={`${devices.length} tracker${devices.length > 1 ? 's' : ''}`} onClick={() => setShowSubscriptions(true)} />
                        <div className="h-px bg-gray-100 mx-4"></div>
                        <AccountMenuItem icon={<Icons.Palette className="w-5 h-5" />} iconBg="bg-purple-100" iconColor="text-purple-600" label="Style" subtitle="Marqueur et carte" onClick={() => setShowStyleView(true)} />
                        <div className="h-px bg-gray-100 mx-4"></div>
                        <AccountMenuItem
                            icon={<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>}
                            iconBg="bg-green-100" iconColor="text-green-600"
                            label={canRefer ? "Parrainage" : "Code promo"}
                            subtitle={canRefer ? "Gagnez 50 CHF par parrainage validé" : "J'ai un code promo à activer"}
                            onClick={() => setShowAffiliation(true)} />
                    </div>

                    {/* Support Section */}
                    <div className="bg-white rounded-2xl shadow-sm overflow-hidden">
                        <AccountMenuItem icon={<Icons.HelpCircle className="w-5 h-5" />} iconBg="bg-emerald-100" iconColor="text-emerald-600" label="Aide & Support" subtitle="Chattez avec nous" onClick={() => setShowChat(true)} />
                        <div className="h-px bg-gray-100 mx-4"></div>
                        <AccountMenuItem icon={<Icons.Mail className="w-5 h-5" />} iconBg="bg-amber-100" iconColor="text-amber-600" label="Nous contacter" subtitle="support@easytrack.ch" onClick={() => window.location.href = 'mailto:support@easytrack.ch'} />
                    </div>

                    {/* Logout */}
                    <button onClick={logout}
                        className="w-full bg-white rounded-2xl shadow-sm p-4 flex items-center justify-center gap-2 text-red-500 hover:bg-red-50 transition-colors active:scale-[0.98]">
                        <Icons.LogOut className="w-5 h-5" />
                        <span className="font-semibold">Se déconnecter</span>
                    </button>

                    {/* Version */}
                    <div className="text-center text-xs text-gray-300 pb-2">
                        EasyTrack v1.9.1
                    </div>
                </div>
            </div>

            {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
        </div>
    );
};

const AccountMenuItem = ({ icon, iconBg, iconColor, label, subtitle, onClick }) => (
    <button onClick={onClick} className="w-full flex items-center gap-3 px-4 py-3.5 hover:bg-gray-50 transition-colors text-left">
        <div className={`w-9 h-9 rounded-xl ${iconBg} flex items-center justify-center flex-shrink-0`}>
            <span className={iconColor}>{icon}</span>
        </div>
        <div className="flex-1 min-w-0">
            <div className="text-sm font-medium text-gray-800">{label}</div>
            {subtitle && <div className="text-xs text-gray-400">{subtitle}</div>}
        </div>
        <Icons.ChevronRight className="w-4 h-4 text-gray-300 flex-shrink-0" />
    </button>
);

const MenuItem = ({ icon, label, onClick }) => (
    <button onClick={onClick} className="menu-item w-full">
        <span className="text-gray-500 mr-3">{icon}</span>
        <span className="flex-1 text-left text-gray-700">{label}</span>
        <Icons.ChevronRight className="w-5 h-5 text-gray-400" />
    </button>
);

// ==================== HOTSPOT VIEW (HEATMAP) ====================
const HotspotView = () => {
    const { user, devices, selectedDevice, setSelectedDevice, badgeLabel, toggleBadgeLabel } = useAppContext();
    const [showAuthModal, setShowAuthModal] = useState(false);
    const [loading, setLoading] = useState(false);
    const [periodPreset, setPeriodPreset] = useState('today');
    const [utcFrom, setUtcFrom] = useState('');
    const [utcTo, setUtcTo] = useState('');
    const [showHotspotOverlay, setShowHotspotOverlay] = useState(false);
    const hotspotOverlayRef = useRef(null);
    useEffect(() => {
        if (!showHotspotOverlay) return;
        const handler = (e) => {
            if (hotspotOverlayRef.current && !hotspotOverlayRef.current.contains(e.target)) setShowHotspotOverlay(false);
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [showHotspotOverlay]);
    const [showDateRange, setShowDateRange] = useState(false);
    const [customDateStart, setCustomDateStart] = useState('');
    const [customDateEnd, setCustomDateEnd] = useState('');
    const mapRef = useRef(null);
    const mapInstance = useRef(null);
    const heatLayerRef = useRef(null);
    const tileLayerRef = useRef(null);

    const formatDateLocal = (d) => {
        const y = d.getFullYear(), m = (d.getMonth()+1).toString().padStart(2,'0'), day = d.getDate().toString().padStart(2,'0');
        return `${y}-${m}-${day}`;
    };
    const formatTimeLocal = (d) => d.getHours().toString().padStart(2,'0') + ':' + d.getMinutes().toString().padStart(2,'0');

    const applyHotspotPreset = useCallback((preset) => {
        setPeriodPreset(preset);
        if (preset !== 'custom') setShowDateRange(false);
        const now = new Date();
        const today = formatDateLocal(now);
        let localDateStart, localDateEnd, localTimeStart, localTimeEnd;
        switch (preset) {
            case 'today':
                localDateStart = today; localDateEnd = today;
                localTimeStart = '00:00'; localTimeEnd = '23:59';
                break;
            case 'yesterday': {
                const yest = formatDateLocal(new Date(now.getTime() - 86400000));
                localDateStart = yest; localDateEnd = yest;
                localTimeStart = '00:00'; localTimeEnd = '23:59';
                break;
            }
            case 'daybeforeyesterday': {
                const dbf = formatDateLocal(new Date(now.getTime() - 2 * 86400000));
                localDateStart = dbf; localDateEnd = dbf;
                localTimeStart = '00:00'; localTimeEnd = '23:59';
                break;
            }
            case 'custom':
                setShowDateRange(true);
                return;
            default: return;
        }
        const f = new Date(`${localDateStart}T${localTimeStart}:00`);
        const t = new Date(`${localDateEnd}T${localTimeEnd}:59`);
        setUtcFrom(f.toISOString().slice(0, 19).replace('T', ' '));
        setUtcTo(t.toISOString().slice(0, 19).replace('T', ' '));
    }, []);

    const applyCustomDateRange = useCallback((start, end) => {
        if (!start || !end) return;
        setCustomDateStart(start);
        setCustomDateEnd(end);
        setPeriodPreset('custom');
        const f = new Date(`${start}T00:00:00`);
        const t = new Date(`${end}T23:59:59`);
        setUtcFrom(f.toISOString().slice(0, 19).replace('T', ' '));
        setUtcTo(t.toISOString().slice(0, 19).replace('T', ' '));
    }, []);

    useEffect(() => { applyHotspotPreset('today'); }, []);
    useEffect(() => { setShowAuthModal(!user); }, [user]);

    useEffect(() => {
        if (!mapRef.current || mapInstance.current) return;
        const { map, tileLayer } = initMapWithPrefs(mapRef.current, { forceType: 'google', forceTint: 'grayDark' });
        mapInstance.current = map;
        tileLayerRef.current = tileLayer;
        return () => {
            if (heatLayerRef.current) { try { map.removeLayer(heatLayerRef.current); } catch(e) {} heatLayerRef.current = null; }
            map.remove();
            mapInstance.current = null;
        };
    }, []);

    const hotspotImei = selectedDevice?.imei;
    const loadHeatmapData = useCallback(async () => {
        if (!user?.token || !hotspotImei || !mapInstance.current || !utcFrom || !utcTo) return;
        setLoading(true);
        try {
            let allPoints = [];
            try {
                const resp = await fetch(`${CONFIG.API_URL}/hotspots/${hotspotImei}?from=${encodeURIComponent(utcFrom)}&to=${encodeURIComponent(utcTo)}`, {
                    headers: { Authorization: `Bearer ${user.token}` }
                });
                if (checkAuth(resp)) { setLoading(false); return; }
                if (resp.ok) {
                    const data = await resp.json();
                    allPoints = data.points || [];
                }
            } catch (e) {}

            if (!mapInstance.current) { setLoading(false); return; }
            if (heatLayerRef.current) { try { mapInstance.current.removeLayer(heatLayerRef.current); } catch(e) {} heatLayerRef.current = null; }

            if (allPoints.length > 0 && mapInstance.current) {
                if (typeof L.heatLayer === 'function') {
                    heatLayerRef.current = L.heatLayer(allPoints, {
                        radius: 10, blur: 15, maxZoom: 17, max: 1.0,
                        gradient: { 0.0: '#0000ff', 0.25: '#00ffff', 0.5: '#00ff00', 0.75: '#ffff00', 1.0: '#ff0000' }
                    }).addTo(mapInstance.current);
                } else {
                    const group = L.layerGroup();
                    allPoints.forEach(([lat, lng]) => {
                        L.circleMarker([lat, lng], { radius: 4, fillColor: '#3B82F6', color: 'transparent', fillOpacity: 0.3, weight: 0 }).addTo(group);
                    });
                    group.addTo(mapInstance.current);
                    heatLayerRef.current = group;
                }
                if (mapInstance.current) mapInstance.current.fitBounds(L.latLngBounds(allPoints.map(p => [p[0], p[1]])), { padding: [30, 30] });
            }
        } catch (e) { console.error('Heatmap error:', e); }
        setLoading(false);
    }, [user, hotspotImei, utcFrom, utcTo]);

    useEffect(() => {
        if (user && hotspotImei && mapInstance.current && utcFrom && utcTo) {
            const t = setTimeout(loadHeatmapData, 300);
            return () => clearTimeout(t);
        }
    }, [user, hotspotImei, utcFrom, utcTo, loadHeatmapData]);

    if (!user) {
        return (
            <AuthModal onClose={() => {}} />
        );
    }

    return (
        <div className="h-full flex flex-col">
            {/* Period selector */}
            <div className="bg-white border-b border-gray-200 px-4 py-2.5 flex flex-col gap-2">
                <div className="flex items-center gap-1.5">
                    {[{ key: 'today', label: 'Auj.' }, { key: 'yesterday', label: 'Hier' }, { key: 'daybeforeyesterday', label: 'Av. hier' }].map(p => (
                        <button key={p.key} onClick={() => applyHotspotPreset(p.key)}
                            className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors ${periodPreset === p.key
                                ? 'bg-blue-500 text-white'
                                : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
                            }`}>
                            {p.label}
                        </button>
                    ))}
                    <div className="w-px h-5 bg-gray-200 mx-0.5"></div>
                    <button onClick={() => applyHotspotPreset('custom')}
                        className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors flex items-center gap-1 ${periodPreset === 'custom'
                            ? 'bg-blue-500 text-white'
                            : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
                        }`}>
                        <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <rect x="3" y="4" width="18" height="18" rx="2"></rect>
                            <path d="M16 2v4M8 2v4M3 10h18"></path>
                        </svg>
                        Dates
                    </button>
                </div>
                {showDateRange && (
                    <div className="flex items-center gap-2">
                        <input type="date" max={new Date().toISOString().split('T')[0]} value={customDateStart}
                            onChange={(e) => { setCustomDateStart(e.target.value); if (customDateEnd && e.target.value) applyCustomDateRange(e.target.value, customDateEnd); }}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                        <span className="text-xs text-gray-400 font-medium">&rarr;</span>
                        <input type="date" min={customDateStart} max={new Date().toISOString().split('T')[0]} value={customDateEnd}
                            onChange={(e) => { setCustomDateEnd(e.target.value); if (customDateStart && e.target.value) applyCustomDateRange(customDateStart, e.target.value); }}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                    </div>
                )}
            </div>

            <div className="flex-1 relative">
                <div ref={mapRef} className="map-container"></div>

                {/* Top right: Map toolbar + reload + overlay */}
                <MapToolbar mapInstance={mapInstance} tileLayerRef={tileLayerRef} />

                {loading && (
                    <div className="absolute inset-0 flex items-center justify-center z-[999] pointer-events-none">
                        <div className="w-10 h-10 border-[3px] border-blue-500 border-t-transparent rounded-full animate-spin"></div>
                    </div>
                )}
            </div>

            {/* Info panel */}
            <div className="bg-white border-t border-gray-100 px-4 py-3">
                <div className="flex items-center gap-1">
                    <span className="text-xs text-gray-400">Faible</span>
                    <div className="flex-1 h-2 rounded-full" style={{ background: 'linear-gradient(to right, #0000ff, #00ffff, #00ff00, #ffff00, #ff0000)' }}></div>
                    <span className="text-xs text-gray-400">Élevé</span>
                </div>
            </div>

            {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
        </div>
    );
};

// ==================== STATS VIEW ====================
const StatsView = () => {
    const { user, selectedDevice } = useAppContext();
    const [showAuthModal, setShowAuthModal] = useState(false);
    const [loading, setLoading] = useState(false);
    const [dailyStats, setDailyStats] = useState([]);
    const [periodDays, setPeriodDays] = useState(30);
    const [customRange, setCustomRange] = useState(false);
    const [dateFrom, setDateFrom] = useState('');
    const [dateTo, setDateTo] = useState('');

    useEffect(() => { setShowAuthModal(!user); }, [user]);

    const formatDateLocal = (d) => {
        const y = d.getFullYear(), m = (d.getMonth()+1).toString().padStart(2,'0'), day = d.getDate().toString().padStart(2,'0');
        return `${y}-${m}-${day}`;
    };

    const statsImei = selectedDevice?.imei;
    const loadStats = useCallback(async () => {
        if (!user?.token || !statsImei) return;
        setLoading(true);
        setDailyStats([]);
        try {
            let url;
            if (customRange && dateFrom && dateTo) {
                url = `${CONFIG.API_URL}/stats/daily?imei=${statsImei}&from=${dateFrom}&to=${dateTo}`;
            } else {
                url = `${CONFIG.API_URL}/stats/daily?imei=${statsImei}&days=${periodDays}`;
            }
            const resp = await fetch(url, { headers: { Authorization: `Bearer ${user.token}` } });
            if (checkAuth(resp)) { setLoading(false); return; }
            if (!resp.ok) { setLoading(false); return; }
            const data = await resp.json();
            if (data.success && data.daily) {
                const stats = data.daily
                    .filter(d => d.distance_km > 0 || d.engine_minutes > 0)
                    .map(d => ({
                        isoDay: d.day,
                        km: d.distance_km,
                        engineMs: d.engine_minutes * 60000,
                        posCount: d.position_count
                    }));
                stats.sort((a, b) => b.isoDay.localeCompare(a.isoDay));
                setDailyStats(stats);
            }
        } catch (e) { console.error('Stats error:', e); }
        setLoading(false);
    }, [user, statsImei, periodDays, customRange, dateFrom, dateTo]);

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

    const formatDuration = (ms) => {
        const totalMin = Math.floor(ms / 60000);
        if (totalMin < 60) return `${totalMin} min`;
        const h = Math.floor(totalMin / 60);
        const m = totalMin % 60;
        return `${h}h${m > 0 ? (m < 10 ? '0' + m : m) : '00'}`;
    };

    const formatDayLabel = (isoDay) => {
        const d = new Date(isoDay + 'T12:00:00');
        const today = formatDateLocal(new Date());
        const yesterday = formatDateLocal(new Date(Date.now() - 86400000));
        if (isoDay === today) return "Aujourd'hui";
        if (isoDay === yesterday) return 'Hier';
        return d.toLocaleDateString('fr-CH', { weekday: 'short', day: 'numeric', month: 'short' });
    };

    const totalKm = dailyStats.reduce((s, d) => s + d.km, 0);
    const totalEngine = dailyStats.reduce((s, d) => s + d.engineMs, 0);
    const maxKm = Math.max(...dailyStats.map(d => d.km), 1);

    if (!user) {
        return (
            <AuthModal onClose={() => {}} />
        );
    }

    return (
        <div className="h-full flex flex-col bg-gray-50">
            {/* Period selector */}
            <div className="bg-white border-b border-gray-200 px-4 py-2.5 flex flex-col gap-2">
                <div className="flex items-center gap-1.5">
                    {[{ days: 7, label: '7j' }, { days: 14, label: '14j' }, { days: 30, label: '30j' }].map(p => (
                        <button key={p.days} onClick={() => { setPeriodDays(p.days); setCustomRange(false); }}
                            className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors ${!customRange && periodDays === p.days ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'}`}>
                            {p.label}
                        </button>
                    ))}
                    <div className="w-px h-5 bg-gray-200 mx-0.5"></div>
                    <button onClick={() => {
                        if (!customRange) {
                            const today = formatDateLocal(new Date());
                            const from30 = formatDateLocal(new Date(Date.now() - 30 * 86400000));
                            setDateFrom(dateFrom || from30);
                            setDateTo(dateTo || today);
                        }
                        setCustomRange(!customRange);
                    }}
                        className={`px-3 py-1.5 rounded-full text-xs font-semibold transition-colors flex items-center gap-1 ${customRange ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'}`}>
                        <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
                        Dates
                    </button>
                    {loading && <div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin ml-auto"></div>}
                </div>
                {customRange && (
                    <div className="flex items-center gap-2">
                        <input type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)} max={dateTo || formatDateLocal(new Date())}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                        <span className="text-xs text-gray-400 font-medium">→</span>
                        <input type="date" value={dateTo} onChange={e => setDateTo(e.target.value)} min={dateFrom} max={formatDateLocal(new Date())}
                            className="flex-1 px-2.5 py-1.5 text-xs border border-gray-200 rounded-lg bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
                    </div>
                )}
            </div>

            {/* Totals */}
            {!loading && selectedDevice && dailyStats.length > 0 && (
                <div className="bg-white border-b border-gray-200 px-4 py-4">
                    <div className="grid grid-cols-2 gap-4">
                        <div className="bg-blue-50 rounded-2xl p-4 text-center">
                            <div className="text-2xl font-bold text-blue-600">{totalKm < 1 ? `${Math.round(totalKm * 1000)}m` : `${totalKm.toFixed(1)} km`}</div>
                            <div className="text-xs text-blue-400 mt-1 font-medium">Distance totale</div>
                        </div>
                        <div className="bg-green-50 rounded-2xl p-4 text-center">
                            <div className="text-2xl font-bold text-green-600">{formatDuration(totalEngine)}</div>
                            <div className="text-xs text-green-400 mt-1 font-medium">Temps moteur</div>
                        </div>
                    </div>
                </div>
            )}

            {/* Daily breakdown */}
            <div className="flex-1 overflow-y-auto">
                {!selectedDevice && (
                    <div className="flex flex-col items-center justify-center py-16 text-gray-400">
                        <Icons.BarChart className="w-12 h-12 mb-3 text-gray-300" />
                        <p className="text-sm">Sélectionnez un véhicule</p>
                    </div>
                )}
                {selectedDevice && !loading && dailyStats.length === 0 && (
                    <div className="flex flex-col items-center justify-center py-16 text-gray-400">
                        <Icons.BarChart className="w-12 h-12 mb-3 text-gray-300" />
                        <p className="text-sm">Aucune donnée sur cette période</p>
                    </div>
                )}
                {selectedDevice && loading && dailyStats.length === 0 && (
                    <div className="flex items-center justify-center py-16">
                        <div className="w-10 h-10 border-[3px] border-blue-500 border-t-transparent rounded-full animate-spin"></div>
                    </div>
                )}
                {dailyStats.map(day => (
                    <div key={day.isoDay} className="bg-white border-b border-gray-100 px-4 py-3 flex items-center gap-3">
                        <div className="w-16 flex-shrink-0">
                            <div className="text-xs font-semibold text-gray-700 capitalize">{formatDayLabel(day.isoDay)}</div>
                        </div>
                        <div className="flex-1 min-w-0">
                            <div className="flex items-center gap-2 mb-1.5">
                                <div className="flex-1 h-2 bg-gray-100 rounded-full overflow-hidden">
                                    <div className="h-full bg-blue-500 rounded-full transition-all" style={{ width: `${Math.max((day.km / maxKm) * 100, 2)}%` }}></div>
                                </div>
                            </div>
                            <div className="flex items-center gap-3 text-xs">
                                <span className="text-blue-600 font-semibold">{day.km < 1 ? `${Math.round(day.km * 1000)}m` : `${day.km.toFixed(1)} km`}</span>
                                <span className="text-gray-300">·</span>
                                <span className="text-green-600 font-medium">{formatDuration(day.engineMs)}</span>
                            </div>
                        </div>
                    </div>
                ))}
            </div>

            {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
        </div>
    );
};

const BottomNavigation = () => {
    const { currentView, navigate, selectedDevice } = useAppContext();
    const selectedStatus = selectedDevice ? getDeviceStatus(selectedDevice.speed || 0, selectedDevice.timestamp || selectedDevice.last_seen_at, selectedDevice.is_active, selectedDevice.ignition, selectedDevice.movement) : null;

    const sideItems = [
        { key: 'replay', icon: Icons.History, label: 'Replay' },
        { key: 'hotspots', icon: Icons.Flame, label: 'Hotspots' },
    ];
    const rightItems = [
        { key: 'stats', icon: Icons.BarChart, label: 'Stats' },
        { key: 'compte', icon: Icons.User, label: 'Compte' },
    ];

    return (
        <nav className="bg-white border-t border-gray-200 px-1 pb-5 safe-area-bottom relative z-[1000]">
            <div className="flex items-end justify-around">
                {sideItems.map(item => {
                    const isActive = currentView === item.key;
                    const IconComponent = item.icon;
                    return (
                        <button key={item.key} onClick={() => navigate(item.key)}
                            className={`flex flex-col items-center justify-center py-2 px-3 transition-colors ${isActive ? 'text-blue-500' : 'text-gray-400'}`}>
                            <IconComponent className="w-5 h-5 mb-0.5" />
                            <span className="text-[10px] font-medium">{item.label}</span>
                        </button>
                    );
                })}

                {/* Live center button */}
                <button onClick={() => navigate('live')}
                    className="flex items-center justify-center transition-all"
                    style={{ transform: 'translateY(-14px)' }}>
                    <div className="w-11 h-11 rounded-full flex items-center justify-center shadow-lg transition-colors scale-[1.3] border-2 border-white"
                        style={{ backgroundColor: selectedStatus ? selectedStatus.colors.main : (currentView === 'live' ? '#3B82F6' : '#1f2937') }}>
                        <svg viewBox="0 0 24 24" width="16" height="16" fill="white" style={{ transform: `rotate(${selectedDevice?.heading || 0}deg)` }}><polygon points="12,2 22,22 12,17 2,22"/></svg>
                    </div>
                </button>

                {rightItems.map(item => {
                    const isActive = currentView === item.key;
                    const IconComponent = item.icon;
                    return (
                        <button key={item.key} onClick={() => navigate(item.key)}
                            className={`flex flex-col items-center justify-center py-2 px-3 transition-colors ${isActive ? 'text-blue-500' : 'text-gray-400'}`}>
                            <IconComponent className="w-5 h-5 mb-0.5" />
                            <span className="text-[10px] font-medium">{item.label}</span>
                        </button>
                    );
                })}
            </div>
        </nav>
    );
};

// ==================== VEHICLE HEADER (shared) ====================
const VehicleHeader = () => {
    const { user, devices, selectedDevice, setSelectedDevice, wsConnected, currentView, hideHeader } = useAppContext();
    const [open, setOpen] = useState(false);
    const [addresses, setAddresses] = useState({});
    const addrCacheRef = useRef({});

    useEffect(() => {
        if (!user?.token || devices.length === 0) return;
        devices.forEach(d => {
            if (!d.latitude || !d.longitude || addrCacheRef.current[d.imei]) return;
            addrCacheRef.current[d.imei] = true;
            fetch(`${CONFIG.API_URL}/geocode?latitude=${d.latitude}&longitude=${d.longitude}`, { headers: { 'Authorization': `Bearer ${user.token}` } })
                .then(r => { if (checkAuth(r)) return null; return r.json(); }).then(data => {
                    if (!data) return;
                    if (data.code === 1 && data.address) setAddresses(prev => ({ ...prev, [d.imei]: data.address }));
                }).catch(() => {});
        });
    }, [user, devices]);

    // Close dropdown on outside click
    useEffect(() => {
        if (!open) return;
        const handler = (e) => { if (!e.target.closest('[data-vehicle-header]')) setOpen(false); };
        document.addEventListener('click', handler);
        return () => document.removeEventListener('click', handler);
    }, [open]);

    if (!user || currentView === 'compte' || hideHeader) return null;

    const selectedStatus = selectedDevice
        ? getDeviceStatus(selectedDevice.speed || 0, selectedDevice.timestamp || selectedDevice.last_seen_at, selectedDevice.is_active, selectedDevice.ignition, selectedDevice.movement)
        : null;

    const addr = selectedDevice ? addresses[selectedDevice.imei] : null;

    const formatLastSeen = (ts) => {
        if (!ts) return '';
        let dateStr = String(ts).replace(' ', 'T');
        if (!dateStr.endsWith('Z') && !dateStr.includes('+')) dateStr += 'Z';
        const dt = new Date(dateStr);
        const day = dt.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: 'Europe/Zurich' });
        const tp = dt.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Zurich' }).split(':');
        return `${day} ${tp[0]}h${tp[1]}`;
    };

    // Status badge colors on blue background - always light/pastel tones
    const getStatusBadge = (status) => {
        const map = {
            moving:   { bg: 'rgba(74,222,128,0.25)', text: '#bbf7d0', dot: '#4ade80' },
            stopped:  { bg: 'rgba(251,191,36,0.25)', text: '#fde68a', dot: '#fbbf24' },
            idling:   { bg: 'rgba(251,191,36,0.20)', text: '#fde68a', dot: '#fbbf24' },
            parked:   { bg: 'rgba(147,197,253,0.25)', text: '#bfdbfe', dot: '#93c5fd' },
            
            disabled: { bg: 'rgba(209,213,219,0.20)', text: '#d1d5db', dot: '#9ca3af' },
        };
        return map[status?.status] || map.disabled;
    };

    return (
        <div data-vehicle-header="" className="z-[1001] relative" style={{ background: 'rgb(37, 99, 235)' }}>

            {/* ── Main row ── */}
            {selectedDevice && selectedStatus ? (() => {
                const badge = getStatusBadge(selectedStatus);
                return (
                    <div className="px-4 pt-3 pb-2.5">
                        {/* Row 1 : plate + model + switch */}
                        <div className="flex items-center gap-3">
                            <div className="flex-1 min-w-0">
                                <div className="flex items-baseline gap-2 min-w-0">
                                    <span className="text-white font-extrabold text-base tracking-wide leading-tight truncate">
                                        {selectedDevice.plate || selectedDevice.name}
                                    </span>
                                    {selectedDevice.vehicle_model && (
                                        <span className="text-blue-200 text-xs font-medium truncate shrink-0">
                                            {selectedDevice.vehicle_model}
                                        </span>
                                    )}
                                </div>
                            </div>

                            {/* Switch button */}
                            {devices.length > 1 && (
                                <button
                                    onClick={() => setOpen(o => !o)}
                                    className="flex items-center gap-1.5 flex-shrink-0 rounded-xl border border-white/20 px-2.5 py-1.5 transition-all active:scale-95"
                                    style={{ background: 'rgba(255,255,255,0.15)', backdropFilter: 'blur(8px)' }}>
                                    <svg className="w-3.5 h-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
                                        <path strokeLinecap="round" strokeLinejoin="round" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
                                    </svg>
                                    <span className="text-white text-xs font-bold">{devices.length}</span>
                                    <Icons.ChevronDown className={`w-3 h-3 text-white/70 transition-transform duration-200 ${open ? 'rotate-180' : ''}`} />
                                </button>
                            )}
                        </div>

                    </div>
                );
            })() : (
                /* ── Loading skeleton ── */
                <div className="px-4 pt-3 pb-3">
                    <div className="flex items-center gap-3">
                        <div className="flex-1 space-y-2">
                            <div className="h-4 rounded-lg overflow-hidden" style={{ background: 'rgba(255,255,255,0.12)', width: '55%' }}>
                                <div className="h-full rounded-lg animate-loading-bar" style={{ background: 'rgba(255,255,255,0.3)' }}></div>
                            </div>
                            <div className="h-2.5 rounded-full overflow-hidden" style={{ background: 'rgba(255,255,255,0.08)', width: '40%' }}>
                                <div className="h-full rounded-full animate-loading-bar" style={{ background: 'rgba(255,255,255,0.2)', animationDelay: '0.2s' }}></div>
                            </div>
                        </div>
                        <div className="w-16 h-8 rounded-xl" style={{ background: 'rgba(255,255,255,0.10)' }}></div>
                    </div>
                </div>
            )}

            {/* ── Dropdown list ── */}
            {open && (
                <div className="absolute top-full left-0 right-0 bg-white shadow-2xl max-h-[60vh] overflow-y-auto z-[1002] border-t border-blue-100"
                    style={{ borderRadius: '0 0 16px 16px' }}>
                    {devices.map((device, i) => {
                        const status = getDeviceStatus(device.speed || 0, device.timestamp || device.last_seen_at, device.is_active, device.ignition, device.movement);
                        const isActive = selectedDevice?.imei === device.imei;
                        return (
                            <button key={device.imei}
                                onClick={() => { setSelectedDevice(device); setOpen(false); }}
                                className={`w-full px-4 py-3.5 flex items-center gap-3 text-left transition-colors ${i < devices.length - 1 ? 'border-b border-gray-50' : ''} ${isActive ? 'bg-blue-50' : 'hover:bg-gray-50 active:bg-gray-100'}`}>

                                {/* Color dot */}
                                <div className="w-2.5 h-2.5 rounded-full flex-shrink-0 mt-0.5" style={{ backgroundColor: status.colors.main }}></div>

                                <div className="flex-1 min-w-0">
                                    <div className="flex items-center gap-1.5 min-w-0">
                                        <span className="font-bold text-gray-800 text-sm truncate">
                                            {device.plate || device.name}
                                        </span>
                                        {device.vehicle_model && (
                                            <span className="text-gray-400 text-xs font-normal truncate shrink">· {device.vehicle_model}</span>
                                        )}
                                        {device.is_shared && (
                                            <span className="text-[10px] bg-blue-100 text-blue-600 px-1.5 py-0.5 rounded-full font-semibold flex-shrink-0">Partagé</span>
                                        )}
                                    </div>
                                    <div className="flex items-center gap-1.5 mt-0.5">
                                        <span className="text-xs font-medium" style={{ color: status.colors.text }}>{status.label}</span>
                                        {status.status === 'moving' && <span className="text-xs text-gray-400">· {Math.round(device.speed || 0)} km/h</span>}
                                        {addresses[device.imei] && <span className="text-[10px] text-gray-400 truncate">· {addresses[device.imei]}</span>}
                                    </div>
                                    {/* date masquée */}
                                </div>

                                {isActive && (
                                    <div className="w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center flex-shrink-0">
                                        <svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                                            <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/>
                                        </svg>
                                    </div>
                                )}
                            </button>
                        );
                    })}
                </div>
            )}
        </div>
    );
};

// ==================== MAIN APP ====================
const App = () => {
    const { currentView } = useAppContext();

    return (
        <div className="h-screen w-full flex flex-col font-sans bg-gray-50">
            <VehicleHeader />
            <div className="flex-1 overflow-hidden">
                {currentView === 'live' && <LiveView />}
                {currentView === 'hotspots' && <HotspotView />}
                {currentView === 'replay' && <ReplayView />}
                {currentView === 'stats' && <StatsView />}
                {currentView === 'compte' && <AccountView />}
            </div>
            <BottomNavigation />
        </div>
    );
};

// App with Provider
const AppWithProvider = () => (
    <AppProvider>
        <App />
    </AppProvider>
);

// Mount
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<AppWithProvider />);
