// Data Loader - Handles loading JSON data files
class DataLoader {
  static async loadApps() {
    try {
      const response = await fetch('/data/apps/generated/apps.json', { cache: 'no-store' });
      if (!response.ok) {
        throw new Error(`Failed to load apps: ${response.status}`);
      }
      const data = await response.json();
      return data.apps;
    } catch (error) {
      console.error('Error loading apps data:', error);
      return [];
    }
  }

  static async loadCategories() {
    try {
      const response = await fetch('/data/apps/apps-categories.json');
      if (!response.ok) {
        throw new Error(`Failed to load categories: ${response.status}`);
      }
      const data = await response.json();
      return data.categories || [];
    } catch (error) {
      console.error('Error loading categories data:', error);
      return [];
    }
  }

  static async loadConfigCategories() {
    try {
      const response = await fetch('/data/apps/apps-config-categories.json');
      if (!response.ok) {
        throw new Error(`Failed to load config categories: ${response.status}`);
      }
      const data = await response.json();
      return data.categories || data;
    } catch (error) {
      console.error('Error loading config categories data:', error);
      return {};
    }
  }

  static async isAppInstalled(appName) {
    try {
      const apps = await this.loadApps();
      const app = apps.find(a => a.name === appName || a.id === appName);
      return app ? app.installed : false;
    } catch (error) {
      console.error(`Error checking if app ${appName} is installed:`, error);
      return false;
    }
  }

  static async getContainers() {
    try {
      // Local implementation - return empty array for now
      
      // TODO: Implement local container detection
      // This would require backend integration to query Docker
      
      // Return empty array for now
      return [];
      
    } catch (error) {
      console.error('Error loading containers:', error);
      return [];
    }
  }

  static async loadSystemConfigs() {
    try {
      
      // Load unified config
      try {
        const response = await fetch('/data/config/generated/configs.json');
        if (response.ok) {
          const data = await response.json();
          return data;
        } else {
          console.warn('Failed to load unified config:', response.status);
          throw new Error(`Failed to load unified config: ${response.status}`);
        }
      } catch (error) {
        console.error('Error loading unified config:', error);
        throw error;
      }
    } catch (error) {
      console.error('Error loading system configs:', error);
      return [];
    }
  }

  static async loadConfig(configType) {
    try {
      
      const response = await fetch(`/data/config/generated/config_${configType}.json`);
      if (!response.ok) {
        throw new Error(`Failed to load ${configType} config: ${response.status}`);
      }
      
      const data = await response.json();
      return data;
    } catch (error) {
      console.error(`Error loading ${configType} data:`, error);
      return null;
    }
  }
}

// Category helper functions
let categories = [];

function getCategoryIcon(categoryId) {
  const category = categories.find(cat => cat.id === categoryId);
  return category ? category.icon : '/core/icons/categories/misc.svg';
}

function getCategoryName(categoryId) {
  const category = categories.find(cat => cat.id === categoryId);
  return category ? category.name : categoryId.charAt(0).toUpperCase() + categoryId.slice(1);
}

function getCategoryDescription(categoryId) {
  const category = categories.find(cat => cat.id === categoryId);
  return category ? category.description : '';
}

// Global apps variable for backward compatibility
let apps = [];

// Global system configs variable
let systemConfigs = [];

// Initialize apps and categories data
async function initializeData() {
  try {
    // Determine current page/route and load appropriate data
    const currentPath = window.location.pathname;
    const searchParams = new URLSearchParams(window.location.search);
    
    if (currentPath === '/dashboard' || currentPath === '/') {
      // Dashboard: Only installed apps + system info (no categories needed)
      await loadDashboardData();
    } else if (currentPath.startsWith('/apps') || currentPath.startsWith('/app') || searchParams.has('apps') || searchParams.has('app')) {
      // Apps page: All apps + categories (no system configs needed)
      await loadAppsPageData();
    } else if (currentPath.startsWith('/admin') || currentPath.startsWith('/config') || currentPath.startsWith('/ssh') || searchParams.has('config')) {
      // Admin area (config + SSH): system configs + apps + categories
      await loadConfigDetailData();
    } else {
      // Default: Load all data for SPA
      await loadAppsPageData();
    }
    
  } catch (error) {
    console.error('Error initializing data:', error);
  }
}

// Dashboard update tracking
let lastDashboardUpdate = null;
let lastInstalledCount = 0;
let lastSystemDataHash = null;

// Dashboard-specific data loading
async function loadDashboardData() {
  
  // Load all apps with installation status
  const allApps = await DataLoader.loadApps();
  apps = allApps; // All apps, not just installed
  categories = []; // Dashboard doesn't need categories
  systemConfigs = []; // Dashboard doesn't need system configs
  
  // Calculate installed apps count
  const installedCount = allApps.filter(app => app.installed === true || app.installed === 'true').length;
  
  // Check for updates
  const hasNewApps = installedCount !== lastInstalledCount;
  const currentTime = new Date();
  
  // Load system info for dashboard display (this waits for DOM elements)
  await loadSystemInfo();
  
  // Update installed count and show update indicator
  const installedCountEl = document.getElementById('installed-count');
  if (installedCountEl) {
    const oldValue = installedCountEl.textContent;
    installedCountEl.textContent = installedCount;
    
    // Don't show notification for automatic updates - only manual reloads
    // if (hasNewApps) {
    //   showUpdateIndicator(`New apps detected: ${installedCount - lastInstalledCount > 0 ? '+' + (installedCount - lastInstalledCount) : 'Updated'}`);
    // }
  } else {
    console.warn('⚠️ installed-count element not found');
  }
  
  // Update last update timestamp
  updateLastUpdateTime(currentTime);
  
  // Store current values for next comparison
  lastInstalledCount = installedCount;
  lastDashboardUpdate = currentTime;
  
  // Start countdown to next automatic update
  startUpdateCountdown();

  // Re-assert the network-attention banner now the dashboard DOM is in place
  // (it leads the dashboard only when there's a real conflict to act on).
  window.networkNotifier?.renderDashboardBanner();

}

// Countdown timer for next automatic update
let updateCountdownInterval = null;

function stopUpdateCountdown() {
  if (updateCountdownInterval) { clearInterval(updateCountdownInterval); updateCountdownInterval = null; }
}

function startUpdateCountdown() {
  // Clear any existing countdown
  if (updateCountdownInterval) {
    clearInterval(updateCountdownInterval);
  }
  
  // Use existing countdown element from HTML
  const countdownEl = document.getElementById('update-countdown');
  if (!countdownEl) {
    console.warn('Update countdown element not found in HTML');
    return;
  }
  
  // Update countdown every second
  updateCountdownInterval = setInterval(() => {
    const now = new Date();
    const nextUpdate = new Date(lastDashboardUpdate.getTime() + 60000); // 60 seconds from last update
    const timeLeft = Math.max(0, Math.floor((nextUpdate - now) / 1000));
    
    if (timeLeft > 0) {
      countdownEl.textContent = `Next update: ${timeLeft}s`;
    } else {
      countdownEl.textContent = 'Updating...';
      // Actually update the dashboard data
      updateDashboardData().then(() => {
        // After update completes, reset the last update time and restart countdown
        lastDashboardUpdate = new Date();
        startUpdateCountdown(); // Restart the countdown
      });
    }
  }, 1000);
}

// Actually update dashboard data elements
async function updateDashboardData() {
  try {
    console.log('🔄 Auto-updating dashboard data...');
    
    // Directly update system info (simpler than full dashboard reload)
    await loadSystemInfo();
    
    // Reload apps to get updated installed count
    const allApps = await DataLoader.loadApps();
    apps = allApps;
    
    // Update installed count
    const installedCount = allApps.filter(app => app.installed === true || app.installed === 'true').length;
    const installedCountEl = document.getElementById('installed-count');
    if (installedCountEl) {
      installedCountEl.textContent = installedCount;
    }
    
    console.log('✅ Dashboard data updated successfully');
  } catch (error) {
    console.error('❌ Auto-update failed:', error);
  }
}

// Update last update timestamp display (simplified - just for tracking)
function updateLastUpdateTime(time) {
  // No timestamp display - just store the time for countdown
}

// Calculate time ago string
function getTimeAgo(date) {
  const now = new Date();
  const diff = Math.floor((now - date) / 1000); // seconds
  
  if (diff < 60) return 'just now';
  if (diff < 3600) return `${Math.floor(diff / 60)} min ago`;
  if (diff < 86400) return `${Math.floor(diff / 3600)} hours ago`;
  return `${Math.floor(diff / 86400)} days ago`;
}

// Apps page data loading
async function loadAppsPageData() {
  
  // Load apps and categories in parallel
  try {
    const [appsData, sidebarCategoriesData, configCategoriesData] = await Promise.all([
      DataLoader.loadApps(),
      DataLoader.loadCategories(),
      DataLoader.loadConfigCategories()
    ]);
    
    
    apps = appsData;
    categories = sidebarCategoriesData; // For sidebar
    systemConfigs = []; // Apps page doesn't need system configs
    
    ////console.log('Apps array:', apps);
    ////console.log('Categories array:', categories);
    
    // Trigger custom event for data loaded
    window.dispatchEvent(new CustomEvent('dataLoaded', { 
      detail: { 
        apps, 
        categories: categories, // For sidebar
        sidebarCategories: categories, // Explicit sidebar categories
        configCategories: configCategoriesData, // For config tabs
        systemConfigs 
      } 
    }));
  } catch (error) {
    console.error('❌ Promise.all failed:', error);
    // Fallback: try loading individually
    try {
      const appsData = await DataLoader.loadApps();
      const sidebarCategoriesData = await DataLoader.loadCategories();
      apps = appsData;
      categories = sidebarCategoriesData;
      systemConfigs = [];
      
      // Trigger custom event for data loaded (fallback)
      window.dispatchEvent(new CustomEvent('dataLoaded', { 
        detail: { 
          apps, 
          categories: categories, // For sidebar
          sidebarCategories: categories, // Explicit sidebar categories
          configCategories: {}, // Empty for fallback
          systemConfigs 
        } 
      }));
    } catch (individualError) {
      console.error('❌ Individual loading failed:', individualError);
      apps = [];
      categories = {};
      systemConfigs = [];
    }
  }
}

// Config detail page data loading
async function loadConfigDetailData() {
  
  // Load everything needed for config management
  const [appsData, categoriesData, systemConfigsData] = await Promise.all([
    DataLoader.loadApps(),
    DataLoader.loadCategories(),
    DataLoader.loadSystemConfigs()
  ]);
  
  apps = appsData;
  categories = categoriesData;
  systemConfigs = systemConfigsData;
  
  ////console.log(`Config detail loaded: ${apps.length} apps, ${categories.length} categories, ${systemConfigs.length} system configs`);
}

// Load system information for dashboard
async function loadSystemInfo() {
  try {
    
    // Add timestamp to prevent browser caching
    const timestamp = Date.now();
    
    // Load system info from files generated by LibrePortal commands
    const systemResponse = await fetch(`/data/system/system_info.json?t=${timestamp}`);
    const diskResponse = await fetch(`/data/system/disk_usage.json?t=${timestamp}`);
    const memoryResponse = await fetch(`/data/system/memory_usage.json?t=${timestamp}`);
    
    if (!systemResponse.ok || !diskResponse.ok || !memoryResponse.ok) {
      throw new Error(`Failed to load system files: system=${systemResponse.status}, disk=${diskResponse.status}, memory=${memoryResponse.status}`);
    }
    
    const systemData = await systemResponse.json();
    const diskData = await diskResponse.json();
    const memoryData = await memoryResponse.json();
    
    
    // Prepare system data
    const osInfo = systemData.os || 'Unknown';
    const cleanOs = osInfo.includes('Ubuntu') ? 'Linux Ubuntu' : 
                   osInfo.includes('Debian') ? 'Linux Debian' :
                   osInfo.includes('CentOS') ? 'Linux CentOS' :
                   osInfo.split(' ')[0]; // Take first part like "Linux"
    
    const diskChartData = diskData.root || diskData;
    
    // Wait for all dashboard elements to be available
    await waitForDashboardElements();
    
    // Now update all elements
    const systemInfoEl = document.getElementById('system-info');
    const uptimeEl = document.getElementById('uptime-info');
    const memoryEl = document.getElementById('memory-info');
    
    if (systemInfoEl) {
      systemInfoEl.textContent = cleanOs;
    } else {
      console.warn('⚠️ system-info element not found');
    }
    
    if (uptimeEl) {
      uptimeEl.textContent = systemData.uptime || 'Unknown';
    } else {
      console.warn('⚠️ uptime-info element not found');
    }
    
    // Update memory info - use the text field which should be properly formatted
    if (memoryEl) {
      memoryEl.textContent = memoryData.text || 'Unknown';
    } else {
      console.warn('⚠️ memory-info element not found');
    }

    // Update disk usage chart. disk_usage.json reports df's 1K blocks, so
    // normalise to bytes — the donut compares against byte-valued app/docker
    // totals. (LiveSystem already emits bytes.)
    updateDiskChart({ used: (diskChartData.used || 0) * 1024, total: (diskChartData.total || 0) * 1024 });
    loadStorageBreakdown();

    // Make the disk card open the full Storage breakdown. onclick (not
    // addEventListener) so a dashboard re-mount can't stack duplicate handlers.
    const diskCardEl = document.getElementById('disk-stat-card');
    if (diskCardEl) {
      const goStorage = () => window.navigateToRoute && window.navigateToRoute('/admin/system/storage');
      diskCardEl.onclick = goStorage;
      diskCardEl.onkeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); goStorage(); } };
    }

    // Attach the 1 Hz live stream so the headline values tick like an
    // instrument. The static fetch above gave us a complete first paint; the
    // stream takes over for tactile updates without a page refresh.
    attachDashboardLive();

  } catch (error) {
    console.error('Error loading system info:', error);
    // Fallback values
    const systemInfoEl = document.getElementById('system-info');
    const uptimeEl = document.getElementById('uptime-info');
    const memoryEl = document.getElementById('memory-info');
    
    if (systemInfoEl) systemInfoEl.textContent = 'Linux';
    if (uptimeEl) uptimeEl.textContent = 'Unknown';
    if (memoryEl) memoryEl.textContent = 'Unknown';
    
    // Mock disk usage for demo - wait for DOM elements
    waitForDashboardElements().then(() => {
      updateDiskChart({ used: 50, total: 100 });
    });
  }
}

// Wire the dashboard's headline values to the 1 Hz live SSE stream. Idempotent
// — repeated calls swap the previous subscription for a fresh one so a SPA
// re-mount of the dashboard doesn't double up. We listen via LiveSystem (a
// singleton EventSource manager), so adding this subscription is essentially
// free even with the Admin → System page open in another tab — same backend
// ticker feeds both. Cleanup hangs off a route-change check: if the dashboard
// DOM goes away we drop the sub on the next sample.
let _dashboardLiveUnsub = null;
function detachDashboardLive() {
  if (typeof _dashboardLiveUnsub === 'function') { try { _dashboardLiveUnsub(); } catch (_) {} _dashboardLiveUnsub = null; }
}
function attachDashboardLive() {
  if (!window.LiveSystem) return;
  if (_dashboardLiveUnsub) { try { _dashboardLiveUnsub(); } catch (_) {} _dashboardLiveUnsub = null; }
  _dashboardLiveUnsub = window.LiveSystem.subscribe((s) => {
    const memoryEl = document.getElementById('memory-info');
    const diskCard = document.getElementById('disk-donut');
    if (!memoryEl && !diskCard) {
      // Dashboard isn't on screen anymore — release the sub.
      if (_dashboardLiveUnsub) { try { _dashboardLiveUnsub(); } catch (_) {} _dashboardLiveUnsub = null; }
      return;
    }
    if (memoryEl && s && s.memory && Number.isFinite(s.memory.total) && s.memory.total > 0) {
      const usedGb = (s.memory.used / 1073741824).toFixed(2);
      const totalGb = (s.memory.total / 1073741824).toFixed(2);
      const pct = (s.memory.percent ?? 0).toFixed(1);
      const next = `${usedGb} GB / ${totalGb} GB - ${pct}%`;
      if (memoryEl.textContent !== next) memoryEl.textContent = next;
    }
    if (diskCard && Array.isArray(s?.disks) && s.disks.length) {
      const root = s.disks.find((d) => d.mount === '/') || s.disks[0];
      if (root && Number.isFinite(root.total) && root.total > 0) {
        updateDiskChart({ used: root.used, total: root.total });
      }
    }
  });
}

// Wait for dashboard elements to be available
function waitForDashboardElements() {
  return new Promise((resolve) => {
    const checkElements = () => {
      const donutEl = document.getElementById('disk-donut');
      const percentText = document.getElementById('disk-percent');
      const systemInfoEl = document.getElementById('system-info');
      const uptimeEl = document.getElementById('uptime-info');
      const memoryEl = document.getElementById('memory-info');
      const installedCountEl = document.getElementById('installed-count');

      if (donutEl && percentText && systemInfoEl && uptimeEl && memoryEl && installedCountEl) {
        resolve();
      } else {
        // Check again after 100ms
        setTimeout(checkElements, 100);
      }
    };
    
    checkElements();
  });
}

// Per-app + Docker storage totals (bytes) for the disk donut breakdown. Fetched
// separately from the live disk used/total because they come from different
// sources (the du-based generator + docker df) and change slowly.
let _diskBreakdown = { apps: 0, docker: 0 };
let _lastDisk = null;   // { used, total } in bytes, last value we drew

// Hand-rolled SVG donut. Segments: [{ color, value }]; the ring fills
// proportionally and any remainder shows as the track.
function _diskDonutSvg(segments) {
  const total = segments.reduce((t, s) => t + (s.value || 0), 0) || 1;
  const r = 42, circ = 2 * Math.PI * r, sw = 13;
  let acc = 0;
  const arcs = segments.map((s) => {
    const frac = (s.value || 0) / total;
    const len = circ * frac;
    const off = circ * (1 - acc);
    acc += frac;
    return len > 0
      ? `<circle cx="60" cy="60" r="${r}" fill="none" stroke="${s.color}" stroke-width="${sw}" stroke-dasharray="${len.toFixed(2)} ${(circ - len).toFixed(2)}" stroke-dashoffset="${off.toFixed(2)}" style="transition:stroke-dasharray .4s ease"/>`
      : '';
  }).join('');
  return `<svg viewBox="0 0 120 120" class="disk-donut-svg" aria-hidden="true">
      <g transform="rotate(-90 60 60)">
        <circle cx="60" cy="60" r="${r}" fill="none" stroke="rgba(var(--text-rgb), 0.08)" stroke-width="${sw}"/>
        ${arcs}
      </g>
    </svg>`;
}

// Fetch the storage breakdown once and redraw the donut with it.
async function loadStorageBreakdown() {
  try {
    const [app, dock] = await Promise.all([
      fetch(`/data/system/app_storage.json?t=${Date.now()}`).then((r) => (r.ok ? r.json() : null)).catch(() => null),
      fetch('/api/system/storage').then((r) => (r.ok ? r.json() : null)).catch(() => null),
    ]);
    _diskBreakdown = {
      apps: (app && Number(app.total_local)) || 0,    // on-disk app data (root device)
      docker: (dock && Number(dock.total)) || 0,      // images + build cache
    };
    if (_lastDisk) updateDiskChart(_lastDisk);
  } catch (_) { /* leave the donut as a plain used/free split */ }
}

// Draw the disk donut: a slice for Apps, Docker, Other (the rest of used) and
// Free, with disk % used in the centre. used/total are bytes.
function updateDiskChart(data) {
  const donutEl = document.getElementById('disk-donut');
  const percentText = document.getElementById('disk-percent');
  if (!donutEl || !percentText) return;   // dashboard not on screen yet

  let used, total;
  if (data && data.root) { used = data.root.used; total = data.root.total; }
  else if (data) { used = data.used; total = data.total; }
  used = Number(used) || 0;
  total = Number(total) || 0;
  if (total <= 0) return;
  _lastDisk = { used, total };

  const percentage = Math.round((used / total) * 100);
  percentText.textContent = `${percentage}%`;
  percentText.style.color = percentage > 90 ? 'var(--status-danger)'
    : percentage > 75 ? 'var(--status-warning)' : '';

  // Apps and Docker are subsets of "used"; clamp so rounding/measurement skew
  // can never make the slices exceed what's actually used.
  const apps = Math.max(0, Math.min(_diskBreakdown.apps, used));
  const docker = Math.max(0, Math.min(_diskBreakdown.docker, used - apps));
  const other = Math.max(0, used - apps - docker);
  const free = Math.max(0, total - used);

  donutEl.innerHTML = _diskDonutSvg([
    { color: 'var(--accent)', value: apps },
    { color: 'var(--status-info)', value: docker },
    { color: 'rgba(var(--text-rgb), 0.35)', value: other },
    { color: 'rgba(var(--text-rgb), 0.12)', value: free },
  ]);
}

// Minimal data loading (fallback)
async function loadMinimalData() {
  
  apps = [];
  categories = [];
  systemConfigs = [];
}

// Preload essential data for smooth navigation
async function preloadOtherPagesData(currentPage) {
  
  try {
    // No preloading needed - data loads fresh each page
    
  } catch (error) {
    console.error('Error preloading navigation data:', error);
  }
}

// Data initialization is now handled by SystemLoader
// initializeData() will be called centrally
