/**
 * GNSS Guard Server - Dashboard JavaScript
 * Multi-asset monitoring with 72h route visualization
 */

// Global state
let map = null;
let currentAsset = null;
let currentData = null;
let assets = [];
let routeMarkers = [];
let sourceMarkers = {};
let showRouteEnabled = true;
let lastFetchSucceeded = false;
let lastValidationTimestamp = null;
let isInitialMapLoad = true; // Only fit bounds on initial load or asset change

// Time mode state
let timeMode = 'now'; // 'now' or 'select'
let selectedTimestamp = null; // Unix timestamp when in 'select' mode
let autoRefreshInterval = null;

// =============================================================================
// AUTO-REFRESH PAGE (every 1 hour to pick up deployments)
// =============================================================================
const PAGE_LOAD_TIME = Date.now();
const AUTO_REFRESH_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
let lastVisibilityCheck = Date.now();

function checkAutoRefresh() {
    const elapsed = Date.now() - PAGE_LOAD_TIME;
    if (elapsed >= AUTO_REFRESH_INTERVAL_MS) {
        console.log('Auto-refreshing page after 1 hour...');
        window.location.reload();
    }
}

// Check for refresh on visibility change (tab becomes active)
document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible') {
        const now = Date.now();
        // Only check if at least 10 seconds since last check (prevents rapid refreshes)
        if (now - lastVisibilityCheck > 10000) {
            lastVisibilityCheck = now;
            checkAutoRefresh();
        }
    }
});

// Periodic check every 5 minutes while tab is active
setInterval(checkAutoRefresh, 5 * 60 * 1000);

// Marker icons for sources
const iconPrimary = makeIcon('violet');
const iconSecondary = makeIcon('grey');
const iconAis = makeIcon('blue');
const iconStarlinkGps = makeIcon('yellow');
const iconStarlinkLocation = makeIcon('green');

const sourceConfig = {
    'nmea_primary': { icon: iconPrimary, name: 'Primary GPS' },
    'nmea_secondary': { icon: iconSecondary, name: 'Secondary GPS' },
    'tm_ais': { icon: iconAis, name: 'TM AIS GPS' },
    'starlink_gps': { icon: iconStarlinkGps, name: 'Starlink GPS' },
    'starlink_location': { icon: iconStarlinkLocation, name: 'Starlink Location' }
};

// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', () => {
    initMap();
    initTabs();
    initTimePicker();
    loadAssets();
    
    // Auto-refresh every 10 seconds (only when in 'now' mode)
    startAutoRefresh();
});

// =============================================================================
// MAP INITIALIZATION
// =============================================================================

function initMap() {
    map = L.map('map', { zoomControl: true }).setView([34.665151, 33.016326], 11);
    
    // CartoDB Dark tiles
    L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
        maxZoom: 19,
        attribution: '&copy; OpenStreetMap & CARTO'
    }).addTo(map);
    
    // Recalculate marker offsets when zoom changes
    map.on('zoomend', () => {
        if (currentData) {
            updateMap(currentData);
        }
    });
}

function makeIcon(color) {
    return new L.Icon({
        iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
        shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',
        iconSize: [25, 41],
        iconAnchor: [12, 41],
        popupAnchor: [1, -34],
        shadowSize: [41, 41]
    });
}

// =============================================================================
// TABS (Mobile)
// =============================================================================

function initTabs() {
    const tabButtons = document.querySelectorAll('.tab-btn');
    tabButtons.forEach(btn => {
        btn.addEventListener('click', () => {
            const tabName = btn.dataset.tab;
            
            // Update button states
            tabButtons.forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
            
            // Update tab content
            document.querySelectorAll('.tab-content').forEach(tab => {
                tab.classList.remove('active');
            });
            document.getElementById(`tab-${tabName}`).classList.add('active');
            
            // Invalidate map size when showing map tab
            if (tabName === 'map' && map) {
                setTimeout(() => map.invalidateSize(), 100);
            }
        });
    });
}

// =============================================================================
// TIME SELECTOR
// =============================================================================

function initTimePicker() {
    // Set default datetime value to now
    const now = new Date();
    const localDatetime = formatDatetimeLocal(now);
    
    const desktopPicker = document.getElementById('selectedDatetime');
    const mobilePicker = document.getElementById('mobileSelectedDatetime');
    
    if (desktopPicker) desktopPicker.value = localDatetime;
    if (mobilePicker) mobilePicker.value = localDatetime;
}

function formatDatetimeLocal(date) {
    // Format date as YYYY-MM-DDTHH:mm for datetime-local input
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    return `${year}-${month}-${day}T${hours}:${minutes}`;
}

function setTimeMode(mode) {
    timeMode = mode;
    
    // Update radio buttons (sync both desktop and mobile)
    document.querySelectorAll('input[name="timeMode"], input[name="timeModeM"]').forEach(radio => {
        radio.checked = radio.value === mode;
    });
    
    // Show/hide datetime picker
    const pickers = ['datetimePicker', 'mobileDatetimePicker'];
    const displays = ['selectedTimeDisplay', 'mobileSelectedTimeDisplay'];
    
    pickers.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.classList.toggle('hidden', mode === 'now');
    });
    
    if (mode === 'now') {
        // Hide the selected time display when switching to 'now'
        displays.forEach(id => {
            const el = document.getElementById(id);
            if (el) el.classList.add('hidden');
        });
        
        // Clear selected timestamp
        selectedTimestamp = null;
        
        // Reset map to fit bounds when switching to 'now'
        isInitialMapLoad = true;
        
        // Restart auto-refresh and fetch current data
        startAutoRefresh();
        fetchData();
        loadRouteData();
    } else {
        // Stop auto-refresh when viewing historical data
        stopAutoRefresh();
    }
}

function onDatetimeChange() {
    // Sync desktop and mobile pickers
    const desktopPicker = document.getElementById('selectedDatetime');
    const mobilePicker = document.getElementById('mobileSelectedDatetime');
    
    // Get the value from whichever picker was changed
    const value = desktopPicker?.value || mobilePicker?.value;
    
    if (desktopPicker) desktopPicker.value = value;
    if (mobilePicker) mobilePicker.value = value;
}

function applySelectedTime() {
    const desktopPicker = document.getElementById('selectedDatetime');
    const value = desktopPicker?.value;
    
    if (!value) {
        alert('Please select a date and time');
        return;
    }
    
    // Convert to Unix timestamp
    const date = new Date(value);
    selectedTimestamp = date.getTime() / 1000;
    
    // Update display
    const displayText = date.toLocaleString('en-US', {
        month: 'short',
        day: 'numeric',
        year: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        hour12: false
    });
    
    const displays = ['selectedTimeDisplay', 'mobileSelectedTimeDisplay'];
    const textEls = ['selectedTimeText', 'mobileSelectedTimeText'];
    
    displays.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.classList.remove('hidden');
    });
    
    textEls.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.textContent = displayText;
    });
    
    // Reset map to fit bounds when applying new time
    isInitialMapLoad = true;
    
    // Fetch historical data
    fetchData();
    loadRouteData();
    
    logEvent('info', `Viewing data at ${displayText}`);
}

function startAutoRefresh() {
    if (autoRefreshInterval) return; // Already running
    autoRefreshInterval = setInterval(() => {
        if (timeMode === 'now') {
            fetchData();
        }
    }, 10000);
}

function stopAutoRefresh() {
    if (autoRefreshInterval) {
        clearInterval(autoRefreshInterval);
        autoRefreshInterval = null;
    }
}

function resetTimeMode() {
    // Reset to 'now' mode (called when switching assets)
    timeMode = 'now';
    selectedTimestamp = null;
    
    // Update UI
    document.querySelectorAll('input[name="timeMode"], input[name="timeModeM"]').forEach(radio => {
        radio.checked = radio.value === 'now';
    });
    
    const pickers = ['datetimePicker', 'mobileDatetimePicker'];
    const displays = ['selectedTimeDisplay', 'mobileSelectedTimeDisplay'];
    
    pickers.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.classList.add('hidden');
    });
    
    displays.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.classList.add('hidden');
    });
    
    // Reset datetime picker to current time
    const now = new Date();
    const localDatetime = formatDatetimeLocal(now);
    const desktopPicker = document.getElementById('selectedDatetime');
    const mobilePicker = document.getElementById('mobileSelectedDatetime');
    if (desktopPicker) desktopPicker.value = localDatetime;
    if (mobilePicker) mobilePicker.value = localDatetime;
    
    // Restart auto-refresh
    startAutoRefresh();
}

// =============================================================================
// ASSET MANAGEMENT
// =============================================================================

async function loadAssets() {
    try {
        const response = await fetch('/api/dashboard/assets');
        if (!response.ok) throw new Error('Failed to load assets');
        
        assets = await response.json();
        renderAssetList();
        populateMobileDropdown();
        
        // Auto-select last asset if available (most recently added)
        if (assets.length > 0) {
            selectAsset(assets[assets.length - 1].name);
        }
    } catch (error) {
        console.error('Error loading assets:', error);
        document.getElementById('assetList').innerHTML = 
            '<div class="asset-loading">Failed to load assets</div>';
    }
}

function renderAssetList() {
    const container = document.getElementById('assetList');
    
    if (assets.length === 0) {
        container.innerHTML = '<div class="asset-loading">No assets registered</div>';
        return;
    }
    
    container.innerHTML = assets.map(asset => {
        // Determine status class:
        // - online + valid = green (online)
        // - online + invalid + distance alert = red (alert)
        // - online + invalid + no distance alert = amber (degraded)
        // - offline = gray (no class)
        let statusClass = '';
        if (asset.is_online) {
            if (asset.is_valid === true) {
                statusClass = 'online';  // green
            } else if (asset.is_valid === false) {
                statusClass = asset.has_distance_alert ? 'alert' : 'degraded';  // red or amber
            } else {
                statusClass = 'online';  // null/unknown - assume ok
            }
        }
        
        const isActive = currentAsset === asset.name;
        
        return `
            <div class="asset-item ${isActive ? 'active' : ''} ${!asset.is_online ? 'offline' : ''}" 
                 onclick="selectAsset('${asset.name}')">
                <div class="asset-name">${asset.name}</div>
                <div class="asset-status">
                    <span class="status-dot ${statusClass}"></span>
                    <span>${asset.is_online ? 'Online' : 'Offline'}</span>
                </div>
            </div>
        `;
    }).join('');
}

function populateMobileDropdown() {
    const select = document.getElementById('mobileAssetSelect');
    select.innerHTML = '<option value="">Select Asset...</option>' + 
        assets.map(asset => `<option value="${asset.name}">${asset.name}</option>`).join('');
    
    if (currentAsset) {
        select.value = currentAsset;
    }
}

function selectAsset(assetName) {
    if (!assetName) return;
    
    currentAsset = assetName;
    
    // Update UI
    renderAssetList();
    document.getElementById('mobileAssetSelect').value = assetName;
    
    // Reset time mode to 'now' when switching assets
    resetTimeMode();
    
    // Clear current data and fetch new
    currentData = null;
    clearSourceMarkers();
    clearRouteMarkers();
    isInitialMapLoad = true; // Reset to fit bounds for new asset
    
    // Show loading state immediately while fetching
    showLoadingState();
    
    fetchData();
    loadRouteData();
}

// =============================================================================
// DATA FETCHING
// =============================================================================

async function fetchData() {
    if (!currentAsset) return;
    
    try {
        // Build URL with optional timestamp parameter
        let url = `/api/dashboard/asset/${currentAsset}/status`;
        if (timeMode === 'select' && selectedTimestamp) {
            url += `?at=${selectedTimestamp}`;
        }
        
        const response = await fetch(url);
        
        if (!response.ok) {
            showDegradedState(`Server error: ${response.status}`);
            return;
        }
        
        const data = await response.json();
        
        if (data.error) {
            showDegradedState(data.error);
            return;
        }
        
        currentData = data;
        lastFetchSucceeded = true;
        
        updateUI(data);
        updateMap(data);
        
        // Log event if validation timestamp changed (only in 'now' mode)
        if (timeMode === 'now' && data.validation_timestamp !== lastValidationTimestamp) {
            lastValidationTimestamp = data.validation_timestamp;
            if (data.has_alert && !data.is_valid && data.max_distance_km !== null) {
                logEvent('crit', `Alert: distance ${data.max_distance_km.toFixed(1)} km`);
            } else if (!data.is_valid) {
                logEvent('warn', 'Validation issue detected');
            } else {
                logEvent('info', 'Cloud status OK');
            }
        }
        
    } catch (error) {
        console.error('Fetch error:', error);
        showDegradedState('Connection failed: ' + error.message);
    }
}

async function loadRouteData() {
    if (!currentAsset) return;
    
    try {
        // Build URL with optional until parameter
        let url = `/api/dashboard/asset/${currentAsset}/route?hours=72`;
        if (timeMode === 'select' && selectedTimestamp) {
            url += `&until=${selectedTimestamp}`;
        }
        
        const response = await fetch(url);
        if (!response.ok) return;
        
        const routeData = await response.json();
        renderRoute(routeData);
        
    } catch (error) {
        console.error('Error loading route:', error);
    }
}

// =============================================================================
// UI UPDATES
// =============================================================================

/**
 * Update both GNSS status pills (desktop and mobile)
 */
function updateStatusPills(status, text) {
    const pills = [
        document.getElementById('desktopStatusPill'),
        document.getElementById('mobileStatusPill')
    ];
    
    pills.forEach(pill => {
        if (!pill) return;
        pill.classList.remove('ok', 'warn', 'crit');
        pill.textContent = text;
        if (status) {
            pill.classList.add(status);
        }
    });
}

function updateUI(data) {
    // Update GNSS status pills
    if (data.has_alert && data.max_distance_km !== null) {
        updateStatusPills('crit', 'GNSS Integrity: At Risk');
    } else if (!data.is_valid) {
        updateStatusPills('warn', 'GNSS Integrity: Degraded');
    } else {
        updateStatusPills('ok', 'GNSS Integrity: Stable');
    }
    
    // Update alert banner
    const alertBanner = document.getElementById('alertBanner');
    const alertDistance = document.getElementById('alert-distance-value');
    
    if (data.has_alert && data.max_distance_km !== null) {
        alertBanner.classList.remove('hidden');
        alertDistance.textContent = `${data.max_distance_km.toFixed(1)} km`;
    } else {
        alertBanner.classList.add('hidden');
    }
    
    // Update sources - pass distance alert state
    const hasDistanceAlert = data.has_alert && data.max_distance_km !== null;
    renderSources(data.sources, hasDistanceAlert);
}

function renderSources(sources, hasDistanceAlert = false) {
    const container = document.getElementById('sourcesContainer');
    const sourceOrder = ['nmea_primary', 'nmea_secondary', 'tm_ais', 'starlink_gps', 'starlink_location'];
    
    container.innerHTML = sourceOrder.map(sourceName => {
        const source = sources[sourceName];
        if (!source) return '';
        
        let cardClass = 'ok';
        let badgeClass = 'badge-healthy';
        let badgeText = 'HEALTHY';
        let coordsText = 'Loading...';
        let updateText = '-';
        let updateClass = '';
        
        if (!source.enabled) {
            cardClass = 'offline';
            badgeClass = 'badge-offline';
            badgeText = 'NOT CONFIGURED';
            coordsText = 'No data source configured.';
        } else if (source.status === 'missing') {
            cardClass = 'crit';
            badgeClass = 'badge-danger';
            badgeText = 'MISSING';
            coordsText = 'No coordinates received.';
            updateClass = 'stale-text';
        } else if (source.status === 'stale' || source.is_stale) {
            cardClass = 'stale';
            badgeClass = 'badge-stale';
            badgeText = 'STALE';
            if (source.coordinates) {
                coordsText = `${source.coordinates.latitude.toFixed(6)}, ${source.coordinates.longitude.toFixed(6)}`;
            }
            updateClass = 'stale-text';
        } else {
            if (source.coordinates) {
                coordsText = `${source.coordinates.latitude.toFixed(6)}, ${source.coordinates.longitude.toFixed(6)}`;
            }
            
            // If distance alert and source has coordinates, mark as AT RISK
            if (hasDistanceAlert && source.coordinates) {
                cardClass = 'crit';
                badgeClass = 'badge-danger';
                badgeText = 'AT RISK';
            }
        }
        
        if (source.last_update_unix) {
            updateText = formatRelativeTime(source.last_update_unix);
        }
        
        return `
            <div class="card ${cardClass}">
                <div class="card-header">
                    <div class="card-title">${source.display_name}</div>
                    <div class="badge ${badgeClass}">${badgeText}</div>
                </div>
                <div class="card-line"><strong>Lat/Lon</strong>: ${coordsText}</div>
                <div class="card-line"><strong>Updated</strong>: <span class="${updateClass}">${updateText}</span></div>
            </div>
        `;
    }).join('');
}

/**
 * Show loading state while fetching data for a new asset
 */
function showLoadingState() {
    // Show neutral loading status
    updateStatusPills(null, 'GNSS Integrity: Loading...');
    
    // Hide alert banner
    document.getElementById('alertBanner').classList.add('hidden');
    
    // Show placeholder source cards
    renderPlaceholderSources('loading');
}

/**
 * Show state when asset has never pushed any validation data
 */
function showNoDataState() {
    lastFetchSucceeded = false;
    
    // Show neutral "no data" status
    updateStatusPills(null, 'GNSS Integrity: No Data');
    
    // Hide alert banner
    document.getElementById('alertBanner').classList.add('hidden');
    
    // Show placeholder source cards indicating awaiting data
    renderPlaceholderSources('nodata');
    
    logEvent('warn', 'Asset has not pushed any validation data yet');
}

/**
 * Render placeholder cards for all sources
 * @param {string} mode - 'loading' or 'nodata'
 */
function renderPlaceholderSources(mode) {
    const container = document.getElementById('sourcesContainer');
    const sourceNames = {
        'nmea_primary': 'Primary GPS',
        'nmea_secondary': 'Secondary GPS',
        'tm_ais': 'TM AIS GPS',
        'starlink_gps': 'Starlink GPS',
        'starlink_location': 'Starlink Location'
    };
    const sourceOrder = ['nmea_primary', 'nmea_secondary', 'tm_ais', 'starlink_gps', 'starlink_location'];
    
    const isLoading = mode === 'loading';
    const badgeText = isLoading ? 'LOADING' : 'AWAITING';
    const coordsText = isLoading ? 'Loading...' : 'Awaiting first update...';
    const updateText = isLoading ? '...' : '—';
    
    container.innerHTML = sourceOrder.map(sourceName => {
        return `
            <div class="card">
                <div class="card-header">
                    <div class="card-title">${sourceNames[sourceName]}</div>
                    <div class="badge badge-offline">${badgeText}</div>
                </div>
                <div class="card-line"><strong>Lat/Lon</strong>: ${coordsText}</div>
                <div class="card-line"><strong>Updated</strong>: <span>${updateText}</span></div>
            </div>
        `;
    }).join('');
}

function showDegradedState(errorMessage) {
    lastFetchSucceeded = false;
    
    // Check if this is a "no data" error
    if (errorMessage && errorMessage.includes('No validation data')) {
        showNoDataState();
        return;
    }
    
    // Update status pills to degraded state
    updateStatusPills('warn', 'GNSS Integrity: Degraded');
    
    // Mark all update times as stale
    document.querySelectorAll('.card-line').forEach(line => {
        if (line.textContent.includes('Updated')) {
            const span = line.querySelector('span');
            if (span) span.classList.add('stale-text');
        }
    });
    
    logEvent('crit', errorMessage);
}

// =============================================================================
// MAP UPDATES
// =============================================================================

// Calculate offset for markers to spread them in a circle when close together
function calculateMarkerOffsets(sourceCoords, zoomLevel) {
    if (Object.keys(sourceCoords).length <= 1) {
        // Single marker, no offset needed
        const result = {};
        for (const [name, coord] of Object.entries(sourceCoords)) {
            result[name] = { lat: coord.lat, lon: coord.lon, offsetLat: 0, offsetLon: 0 };
        }
        return result;
    }
    
    // Calculate centroid
    let sumLat = 0, sumLon = 0, count = 0;
    for (const coord of Object.values(sourceCoords)) {
        sumLat += coord.lat;
        sumLon += coord.lon;
        count++;
    }
    const centroidLat = sumLat / count;
    const centroidLon = sumLon / count;
    
    // Check if markers are close together (within ~50 meters)
    const closeThreshold = 0.0005; // ~50m in degrees
    let maxDist = 0;
    for (const coord of Object.values(sourceCoords)) {
        const dist = Math.sqrt(
            Math.pow(coord.lat - centroidLat, 2) + 
            Math.pow(coord.lon - centroidLon, 2)
        );
        maxDist = Math.max(maxDist, dist);
    }
    
    // If markers are spread out enough, don't offset
    if (maxDist > closeThreshold) {
        const result = {};
        for (const [name, coord] of Object.entries(sourceCoords)) {
            result[name] = { lat: coord.lat, lon: coord.lon, offsetLat: 0, offsetLon: 0 };
        }
        return result;
    }
    
    // Calculate offset radius based on zoom level (smaller offset when zoomed in)
    // At zoom 15, offset ~30m; at zoom 10, offset ~100m
    const baseOffset = 0.0003; // ~30m base offset
    const zoomFactor = Math.pow(2, 15 - Math.min(zoomLevel, 18));
    const offsetRadius = baseOffset * zoomFactor;
    
    // Arrange markers in a circle around centroid
    const result = {};
    const sourceNames = Object.keys(sourceCoords);
    const angleStep = (2 * Math.PI) / sourceNames.length;
    
    sourceNames.forEach((name, index) => {
        const angle = angleStep * index - Math.PI / 2; // Start from top
        const offsetLat = offsetRadius * Math.cos(angle);
        const offsetLon = offsetRadius * Math.sin(angle) * 1.5; // Adjust for latitude distortion
        
        result[name] = {
            lat: centroidLat + offsetLat,
            lon: centroidLon + offsetLon,
            offsetLat: offsetLat,
            offsetLon: offsetLon,
            originalLat: sourceCoords[name].lat,
            originalLon: sourceCoords[name].lon
        };
    });
    
    return result;
}

function updateMap(data) {
    clearSourceMarkers();
    
    const sources = data.sources || {};
    const allCoords = [];
    const sourceCoords = {};
    
    // First pass: collect all valid coordinates
    Object.entries(sources).forEach(([sourceName, sourceData]) => {
        if (sourceData.coordinates && sourceData.coordinates.latitude && sourceData.coordinates.longitude) {
            const lat = sourceData.coordinates.latitude;
            const lon = sourceData.coordinates.longitude;
            if (sourceConfig[sourceName]) {
                sourceCoords[sourceName] = { lat, lon };
                allCoords.push([lat, lon]);
            }
        }
    });
    
    // Calculate offsets for overlapping markers
    const zoomLevel = map.getZoom() || 13;
    const offsetPositions = calculateMarkerOffsets(sourceCoords, zoomLevel);
    
    // Second pass: add markers with calculated positions
    Object.entries(sources).forEach(([sourceName, sourceData]) => {
        if (sourceData.coordinates && sourceData.coordinates.latitude && sourceData.coordinates.longitude) {
            const config = sourceConfig[sourceName];
            const position = offsetPositions[sourceName];
            
            if (config && position) {
                // Build popup with original coordinates
                const origLat = sourceData.coordinates.latitude;
                const origLon = sourceData.coordinates.longitude;
                const popupContent = `<b>${config.name}</b><br>Lat: ${origLat.toFixed(6)}<br>Lon: ${origLon.toFixed(6)}`;
                
                const marker = L.marker([position.lat, position.lon], { icon: config.icon })
                    .bindPopup(popupContent)
                    .addTo(map);
                sourceMarkers[sourceName] = marker;
            }
        }
    });
    
    // Fit map to show all markers (only on initial load or asset change, not on refresh)
    if (isInitialMapLoad) {
        if (allCoords.length > 0) {
            const bounds = L.latLngBounds(allCoords);
            map.fitBounds(bounds, {
                padding: [50, 50],  // Add padding around markers
                maxZoom: 15        // Don't zoom in too much when markers are close
            });
        } else if (currentData && currentData.map_center && currentData.map_center.latitude && currentData.map_center.longitude) {
            // Fallback to center if no markers
            map.setView([currentData.map_center.latitude, currentData.map_center.longitude], 13);
        }
        isInitialMapLoad = false; // Don't auto-zoom on subsequent refreshes
    }
}

function clearSourceMarkers() {
    Object.values(sourceMarkers).forEach(marker => {
        map.removeLayer(marker);
    });
    sourceMarkers = {};
}

// =============================================================================
// ROUTE VISUALIZATION
// =============================================================================

function renderRoute(routeData) {
    clearRouteMarkers();
    
    if (!showRouteEnabled || !routeData || routeData.length === 0) return;
    
    // Create small circle markers for route points
    routeData.forEach(point => {
        let color;
        let statusText;
        
        switch (point.status) {
            case 'valid':
                color = '#1fad3a';
                statusText = 'Valid';
                break;
            case 'degraded':
                color = '#ffa726';
                statusText = 'Degraded';
                break;
            case 'alert':
                color = '#c62828';
                statusText = 'Alert';
                break;
            default:
                color = '#9aa3b8';
                statusText = 'Unknown';
        }
        
        const marker = L.circleMarker([point.latitude, point.longitude], {
            radius: 5,
            fillColor: color,
            color: color,
            weight: 1,
            opacity: 0.8,
            fillOpacity: 0.6
        }).addTo(map);
        
        // Create detailed popup
        const popupContent = `
            <div class="route-popup">
                <div class="popup-header">${formatTimestamp(point.timestamp)}</div>
                <div class="popup-row"><strong>Status:</strong> <span class="status-${point.status}">${statusText}</span></div>
                <div class="popup-row"><strong>Lat/Lon:</strong> ${point.latitude.toFixed(6)}, ${point.longitude.toFixed(6)}</div>
                ${point.sources_missing?.length ? `<div class="popup-row"><strong>Missing:</strong> ${point.sources_missing.join(', ')}</div>` : ''}
                ${point.sources_stale?.length ? `<div class="popup-row"><strong>Stale:</strong> ${point.sources_stale.join(', ')}</div>` : ''}
                ${point.max_distance_m > point.threshold_m ? `<div class="popup-row"><strong>Distance:</strong> ${(point.max_distance_m/1000).toFixed(2)} km</div>` : ''}
            </div>
        `;
        
        marker.bindPopup(popupContent);
        routeMarkers.push(marker);
    });
}

function clearRouteMarkers() {
    routeMarkers.forEach(marker => {
        map.removeLayer(marker);
    });
    routeMarkers = [];
}

function toggleRoute() {
    showRouteEnabled = document.getElementById('showRoute').checked;
    if (showRouteEnabled) {
        loadRouteData();
    } else {
        clearRouteMarkers();
    }
}

// =============================================================================
// EVENT LOGGING
// =============================================================================

function logEvent(level, message) {
    const log = document.getElementById('eventLog');
    const now = new Date();
    const time = now.toTimeString().slice(0, 8);
    
    const levelMap = {
        'info': 'INFO',
        'warn': 'WARN',
        'crit': 'CRIT'
    };
    
    const event = document.createElement('div');
    event.className = `event level-${level}`;
    event.innerHTML = `<span class="level">${levelMap[level]}</span> [${time}] ${message}`;
    
    // Insert after title
    const title = log.querySelector('.event-log-title');
    if (title.nextSibling) {
        log.insertBefore(event, title.nextSibling);
    } else {
        log.appendChild(event);
    }
    
    // Keep only 3 events
    const events = log.querySelectorAll('.event');
    while (events.length > 3) {
        const lastEvent = log.querySelector('.event:last-of-type');
        if (lastEvent) lastEvent.remove();
        else break;
    }
}

// =============================================================================
// UTILITIES
// =============================================================================

function formatRelativeTime(unixTimestamp) {
    const now = Date.now() / 1000;
    const diff = now - unixTimestamp;
    
    if (diff < 60) return `${Math.floor(diff)}s ago`;
    if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
    if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
    return `${Math.floor(diff / 86400)}d ago`;
}

function formatTimestamp(isoString) {
    const date = new Date(isoString);
    return date.toLocaleString('en-US', {
        month: 'short',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        hour12: false
    });
}

// =============================================================================
// AUTHENTICATION
// =============================================================================

async function logout() {
    try {
        await fetch('/logout', { method: 'POST' });
        window.location.href = '/login';
    } catch (error) {
        console.error('Logout error:', error);
        window.location.href = '/login';
    }
}

