// Enhanced App Manager with Tabbed Interface
// Integrates app management with task history

class AppTabbedManager {
  constructor() {
    // console.log('🔍 AppTabbedManager constructor called');
    // console.log('🔍 URL in constructor:', window.location.href);
    // console.log('🔍 Search params in constructor:', window.location.search);
    
    // Store original URL for task parameter detection
    this.originalUrl = window.location.href;
    this.originalSearch = window.location.search;
    
    // Check sessionStorage for task parameter (fallback)
    const sessionTaskId = sessionStorage.getItem('pendingTaskId');
    // console.log('🔍 Session storage task ID:', sessionTaskId);
    
    // Debug: Check if task parameter exists in original URL
    const originalParams = new URLSearchParams(this.originalSearch);
    const originalTaskId = originalParams.get('task');
    // console.log('🔍 Original task ID in constructor:', originalTaskId);
    
    // Try to get task parameter from performance navigation if available
    if (performance && performance.getEntriesByType) {
      const navigationEntries = performance.getEntriesByType('navigation');
      if (navigationEntries.length > 0) {
        const navEntry = navigationEntries[0];
        // console.log('🔍 Navigation entry URL:', navEntry.name);
        const navParams = new URLSearchParams(new URL(navEntry.name).search);
        const navTaskId = navParams.get('task');
        // console.log('🔍 Navigation task ID:', navTaskId);
      }
    }
    
    this.currentApp = this.getAppFromURL();
    this.currentTab = this.getTabFromURL();
    this.tasksManager = new TasksManager();
    this.appsManager = new AppsManager();
    this.initialized = false;
    
    // Button state management
    this.disabledButtons = new Set();
    this.activeTaskId = null;
    // Track running tasks. Key is `${appName}|${action}` (using `|` so app names with
    // hyphens don't collide). Value is { taskId, appName, action } so callers never
    // have to parse the key.
    this.runningTasks = new Map();
  }

  // Build the runningTasks key for an (app, action) pair. Use `|` since `-` and `_`
  // appear in app/action strings (e.g. 'delete_all').
  taskKey(appName, action) {
    return `${appName}|${action}`;
  }

  // Find the most recent running task for the given app, or null.
  getRunningTaskForApp(appName) {
    if (!appName) return null;
    for (const info of this.runningTasks.values()) {
      if (info.appName === appName) return info;
    }
    return null;
  }

  // Switch the manager to a new app, clearing DOM-bound state from the previous app
  // and re-evaluating tab/button state for the new one. Callers (e.g. apps-manager)
  // should use this instead of mutating `currentApp` directly so disabled tabs from
  // app A don't bleed into app B's view.
  setCurrentApp(appName) {
    if (this.currentApp === appName) return;

    // console.log('🔄 setCurrentApp: switching from %s to %s', this.currentApp, appName);
    this.currentApp = appName;

    // Before clearing disabled button references, restore any static backup action buttons
    // that may have spinners/disabled state from a previous app
    const backupActions = document.querySelectorAll('.backup-actions button');
    backupActions.forEach(button => {
      if (button && button.dataset.originalContent) {
        button.disabled = false;
        button.classList.remove('disabled', 'task-running');
        button.innerHTML = button.dataset.originalContent;
        delete button.dataset.originalContent;
      }
    });

    this.disabledButtons.clear();
    this.activeTaskId = null;
    this.enableTabs();

    const running = this.getRunningTaskForApp(appName);
    // console.log('🔍 setCurrentApp: running task for %s = %o', appName, running);
    if (running) {
      this.activeTaskId = running.taskId;
    }
  }

  // Get app name from URL parameter
  getAppFromURL() {
    // Path-based /app/<name> first, then legacy ?app= / ?=name.
    let appName = window.location.pathname.replace(/^\/app\/?/, '').split('/')[0];
    appName = appName ? decodeURIComponent(appName) : '';

    if (!appName) {
      const urlParams = new URLSearchParams(window.location.search);
      appName = urlParams.get('app');
      if (!appName && window.location.search.includes('?=')) {
        appName = window.location.search.split('?=')[1].split('&')[0];
      }
    }
    
    // console.log('🔍 Original app name from URL:', appName);
    
    // Convert full app name to slug for task filtering
    if (appName && window.apps) {
      const appData = window.apps.find(app => {
        // Extract slug from command
        const command = app.command || '';
        const parts = command.split(' ');
        return parts[parts.length - 1] === appName;
      });
      
      if (appData) {
        const command = appData.command || '';
        const parts = command.split(' ');
        const slug = parts[parts.length - 1]; // Return the slug
        // console.log('🔄 Converted to slug:', slug, 'from appData:', appData.name);
        return slug;
      } else {
        // console.log('⚠️ No app data found for:', appName);
      }
    }
    
    // console.log('🔄 Returning original app name:', appName);
    return appName;
  }

  // Get tab from URL parameter
  getTabFromURL() {
    const currentUrl = window.location.href;
    const urlParams = new URLSearchParams(window.location.search);
    const tab = urlParams.get('tab') || 'config';
    // console.log('🔍 getTabFromURL debug:', {
      //currentUrl: currentUrl,
      //search: window.location.search,
      //tabParam: urlParams.get('tab'),
      //defaultTab: 'config',
      //finalTab: tab === 'logs' ? 'tasks' : tab
    //});
    // Convert "logs" to "tasks" for backward compatibility
    return tab === 'logs' ? 'tasks' : tab;
  }

  // Check if we're on an app page before doing anything
  isAppPage() {
    const pathname = window.location.pathname;
    const search = window.location.search;
    
    // Only individual app pages (/app?=appname), NOT the apps listing page (/apps)
    return (pathname.startsWith('/app') && !pathname.startsWith('/apps') || 
            pathname.endsWith('/index.html') || pathname === '/index.html' ||
            search.includes('app=') ||
            search.includes('?=')); // Old format app pages
  }

  // Update URL with app and tab — path-based: /app/<app>?tab=<tab>&task=<task>.
  updateURL(app = null, tab = null) {
    // Only update URLs on app pages - prevent interference with other pages.
    if (!this.isAppPage()) return;

    const params = new URLSearchParams(window.location.search);
    const fromPath = window.location.pathname.replace(/^\/app\/?/, '').split('/')[0];
    const currentApp = app || (fromPath ? decodeURIComponent(fromPath) : '') || this.currentApp;
    if (!currentApp) return;

    const q = new URLSearchParams();
    const finalTab = tab || params.get('tab');
    if (finalTab) q.set('tab', finalTab);
    // Keep a deep-linked task only when staying on the same app (tab-only update).
    if (!app) {
      const task = params.get('task');
      if (task) q.set('task', task);
    }

    const qs = q.toString();
    window.history.replaceState({}, '', `/app/${encodeURIComponent(currentApp)}${qs ? '?' + qs : ''}`);
  }

  // Update current app and refresh content
  updateApp(newAppName) {
    this.setCurrentApp(newAppName);
    // Reset to the config tab on the path-based app URL.
    history.replaceState({}, '', `/app/${encodeURIComponent(newAppName)}?tab=config`);
    this.switchTab('config');
  }

  // Switch between tabs
  switchTab(tabId) {
    // console.log('🔄 switchTab called with:', tabId);
    // console.log('🔍 Current currentApp before switch:', this.currentApp);
    // console.log('🔍 Current URL when switching:', window.location.href);
    // console.log('🔍 URL search when switching:', window.location.search);
    
    // Remove active class from all main navigation tabs
    document.querySelectorAll('.main-tab-button').forEach(btn => {
      btn.classList.remove('active');
    });
    
    // Hide all tab panes
    document.querySelectorAll('.tab-pane').forEach(pane => {
      pane.classList.remove('active');
    });
    
    // Add active class to selected main navigation tab
    const selectedTab = document.querySelector(`.main-tab-button[data-tab="${tabId}"]`);
    if (selectedTab) {
      selectedTab.classList.add('active');
      //// // console.log('✅ Tab button activated:', tabId);
    } else {
      console.warn('⚠️ Main navigation tab button not found:', tabId);
    }
    
    // Add active class to selected tab pane
    const selectedPane = document.getElementById(`${tabId}-tab`);
    if (selectedPane) {
      selectedPane.classList.add('active');
      //// // console.log('✅ Tab pane activated:', tabId);
    } else {
      console.warn('⚠️ Tab pane not found:', tabId);
    }
    
    // Update URL (only tab, not app) - but only on app pages
    if (this.isAppPage()) {
      // console.log('🔄 About to updateURL with tab:', tabId);
      this.updateURL(null, tabId);
    }
    
    // Load tab-specific content
    // console.log('🔄 About to load tab content for tab:', tabId, 'with currentApp:', this.currentApp);
    this.loadTabContent(tabId);
  }

  // Load content for specific tab
  async loadTabContent(tabId) {
    const actualTabId = tabId === 'logs' ? 'tasks' : tabId;

    const currentAppFromUrl = this.getAppFromURL();
    // console.log('📂 loadTabContent: tabId=%s, currentApp=%s, fromUrl=%s',
    //   tabId, this.currentApp, currentAppFromUrl);
    
    // Update currentApp if URL has different app name. Route through setCurrentApp
    // so any disable state from the previous app gets cleared before we render.
    if (currentAppFromUrl && currentAppFromUrl !== this.currentApp) {
      this.setCurrentApp(currentAppFromUrl);
    }
    
    // Ensure app detail view is shown and app is loaded before loading tab content
    if (!this.currentApp || this.currentApp === 'null') {
      console.warn('⚠️ No current app set, cannot load tab content');
      return;
    }

    // Toggle the Tools tab button visibility based on whether this app has
    // any tools. Tools-less apps simply don't see the tab. If the user
    // landed on the tools tab via a deep link for such an app, redirect
    // them to config so they're not staring at an empty pane.
    if (window.toolsManager) {
      const toolsResult = await window.toolsManager.prepare(this.currentApp);
      if (actualTabId === 'tools' && (!toolsResult || toolsResult.tools.length === 0)) {
        return this.switchTab('config');
      }
    }

    // Routing tab is Traefik-only — show on Traefik, hide everywhere else.
    const isTraefik = this.currentApp === 'traefik';
    document.querySelectorAll('[data-tab="routing"]').forEach(btn => {
      btn.style.display = isTraefik ? '' : 'none';
    });
    if (actualTabId === 'routing' && !isTraefik) {
      return this.switchTab('config');
    }
    
    // Make sure app detail view is visible and app is loaded
    if (window.appsManager) {
      // Use showAppDetail to ensure proper initialization (same as config tab)
      // console.log('🔄 Ensuring app detail is loaded for:', this.currentApp);
      window.appsManager.showAppDetail(this.currentApp);
      
      // Wait a bit for DOM to be ready after app detail is rendered
      await new Promise(resolve => setTimeout(resolve, 200));
    }
    
    switch (actualTabId) {
      case 'tasks':
        // console.log('🔄 loadTabContent: Loading tasks for app:', this.currentApp);
        await this.loadAppTasks();
        break;
      case 'backups':
        // console.log('🔄 loadTabContent: Loading backups for app:', this.currentApp);
        await this.loadAppBackups();
        // IMPORTANT: Re-apply button state if there are running tasks
        this.restoreButtonState();
        break;
      case 'services':
        if (window.servicesManager) {
          await window.servicesManager.load(this.currentApp);
        }
        this.restoreButtonState();
        break;
      case 'tools':
        if (window.toolsManager) {
          await window.toolsManager.load(this.currentApp);
        }
        this.restoreButtonState();
        break;
      case 'routing':
        if (window.routingManager) {
          await window.routingManager.load(this.currentApp);
        }
        this.restoreButtonState();
        break;
      case 'config':
        // Config is already handled by showAppDetail above
        // console.log('🔧 Config content already loaded by showAppDetail');
        // IMPORTANT: Re-apply button state if there are running tasks
        this.restoreButtonState();
        break;
      default:
        // Config is handled by existing app management system
        break;
    }

    // Tear down the services tab (timers + SSE) when switching away.
    if (actualTabId !== 'services' && window.servicesManager) {
      window.servicesManager.unload();
    }
    if (actualTabId !== 'tools' && window.toolsManager) {
      window.toolsManager.unload();
    }
  }

  // Load tasks specific to current app
  async loadAppTasks() {
    // console.log('🔄 loadAppTasks called, currentApp:', this.currentApp);
    
    // Show loading spinner by showing the initial loading state
    const tasksContainer = document.getElementById('app-tasks');
    if (tasksContainer) {
      tasksContainer.innerHTML = `
        <div class="loading-content" style="
          text-align: center; 
          padding: 40px 20px;
          color: #ffffff;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          min-height: 200px;
          background: rgba(0, 0, 0, 0.2);
          border-radius: 8px;
          border: 1px solid rgba(255, 255, 255, 0.1);
          margin: 16px;
        ">
          <div class="loading-spinner" style="
            width: 24px; 
            height: 24px; 
            border: 3px solid rgba(52, 152, 219, 0.3); 
            border-top: 3px solid #3498db; 
            border-radius: 50%; 
            animation: spin 1s linear infinite;
            margin-bottom: 16px;
          "></div>
          <p>Loading tasks...</p>
        </div>
      `;
    }
    
    if (!this.currentApp) {
      if (tasksContainer) {
        tasksContainer.innerHTML = '<p style="color: #888;">No app selected.</p>';
      }
      return;
    }

    try {
      // Load all tasks
      // console.log('🔄 Loading tasks...');
      // console.log('🔍 Using currentApp for filtering:', this.currentApp);
      await this.tasksManager.loadTasks();
      const allTasks = this.tasksManager.tasks || [];
      // console.log('📊 All tasks loaded:', allTasks.length);
      // console.log('📋 All tasks data:', allTasks);
      // console.log('📋 Sample task app names:', allTasks.slice(0, 3).map(t => t.app));
      
      // Filter tasks for current app
      const appTasks = allTasks.filter(task => task.app === this.currentApp);
      // console.log('🎯 Filtering tasks for app:', this.currentApp);
      // console.log('📋 Available task.app values:', [...new Set(allTasks.map(t => t.app))]);
      // console.log('🎯 Filtered tasks for', this.currentApp, ':', appTasks.length);
      
      // Debug: Show what would match if we used different app names
      // console.log('🔍 Debug - Testing different app names:');
      ['libreportal', 'fail2ban', 'LibrePortal', 'Fail2Ban'].forEach(testApp => {
        const testTasks = allTasks.filter(task => task.app === testApp);
        // console.log(`  - "${testApp}": ${testTasks.length} tasks`);
      });
      
      if (appTasks.length === 0) {
        // console.log('⚠️ No tasks found for', this.currentApp, '- checking if tasks have different app names');
        // Show some task details for debugging
        if (allTasks.length > 0) {
          // console.log('📋 Sample tasks:', allTasks.slice(0, 3).map(t => ({ id: t.id, app: t.app, command: t.command })));
        }
        tasksContainer.innerHTML = `<p style="color: #888;">No tasks found for ${this.currentApp}.</p>`;
        return;
      }

      // Setup global functions for task interactions
      this.tasksManager.setupGlobalFunctions();

      // Render app-specific tasks
      const tasksHtml = appTasks.map(task => this.tasksManager.renderTask(task)).join('');
      tasksContainer.innerHTML = tasksHtml;
      
      // Setup app-specific task interactions (separate from main tasks system)
      this.setupAppTaskFunctions();
      
      // Handle pending task ID from URL parameter
      if (this.pendingTaskId) {
        // console.log('🔍 Handling pending task ID after tasks loaded:', this.pendingTaskId);
        setTimeout(() => {
          if (typeof window.toggleAppTaskDetails === 'function') {
            // console.log('🔍 Opening task details for pending task:', this.pendingTaskId);
            window.toggleAppTaskDetails(this.pendingTaskId);
            
            // Scroll to the task element after opening details
            this.scrollToTask(this.pendingTaskId);
            
            this.pendingTaskId = null; // Clear pending task ID
          }
        }, 500); // Wait a bit for DOM to be ready
      }
      
      // Task events are handled by individual task components
      // No additional initialization needed
      
    } catch (error) {
      console.error('AppTabbedManager: Error loading app tasks:', error);
      tasksContainer.innerHTML = `<p style="color: #dc3545;">Error loading tasks: ${error.message}</p>`;
    }
  }

  // Scroll to specific task element with smooth animation
  scrollToTask(taskId) {
    // console.log('🔍 Scrolling to task:', taskId);
    
    // Find the task element by ID or data attribute
    let taskElement = document.getElementById(`task-${taskId}`);
    
    // If not found by ID, try to find by data-task-id attribute
    if (!taskElement) {
      taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
    }
    
    // If still not found, try to find the task details element
    if (!taskElement) {
      const detailsElement = document.getElementById(`details-${taskId}`);
      if (detailsElement) {
        taskElement = detailsElement.closest('.task-item');
      }
    }
    
    if (taskElement) {
      // console.log('🔍 Found task element, scrolling to it:', taskElement);
      
      // Smooth scroll to the task element
      taskElement.scrollIntoView({
        behavior: 'smooth',
        block: 'center', // Center the task in the viewport
        inline: 'nearest'
      });
      
      // Add a highlight effect to make the task more visible
      taskElement.classList.add('task-highlighted');
      
      // Remove the highlight after 3 seconds
      setTimeout(() => {
        taskElement.classList.remove('task-highlighted');
      }, 3000);
      
    } else {
      console.warn('⚠️ Task element not found for scrolling:', taskId);
    }
  }

  // Setup app-specific task functions to avoid conflicts with main tasks page
  setupAppTaskFunctions() {
    // Create app-specific toggleTaskDetails function
    window.toggleAppTaskDetails = (taskId) => {
      // console.log('🔍 App-specific toggleTaskDetails called for:', taskId);
      
      const details = document.getElementById(`details-${taskId}`);
      const toggleBtn = document.querySelector(`.task-btn.toggle-details[onclick*="toggleTaskDetails('${taskId}')"]`);
      
      if (details) {
        const isOpen = details.style.display === 'block';
        
        // Close all other task details and reset their buttons
        document.querySelectorAll('.task-details').forEach(d => {
          if (d.id !== `details-${taskId}`) {
            d.style.display = 'none';
            d.classList.remove('task-details-open');
          }
        });
        
        document.querySelectorAll('.task-btn.toggle-details').forEach(btn => {
          btn.classList.remove('expanded');
        });
        
        if (isOpen) {
          details.style.display = 'none';
          details.classList.remove('task-details-open');
          if (toggleBtn) toggleBtn.classList.remove('expanded');
        } else {
          details.style.display = 'block';
          details.classList.add('task-details-open');
          if (toggleBtn) toggleBtn.classList.add('expanded');
          
          // Auto-load logs when opened
          if (this.tasksManager && this.tasksManager.loadTaskLogs) {
            this.tasksManager.loadTaskLogs(taskId);
          }
          
          // Update URL to include task parameter
          const currentUrl = window.location.href;
          const urlParams = new URLSearchParams(currentUrl.search);
          
          // Get current app from AppTabbedManager
          const currentApp = this.currentApp || '';
          
          // Construct proper URL with correct parameter order
          const newUrl = `/app/${currentApp}?tab=tasks&task=${taskId}`;
          // console.log('🔍 Updating URL with task parameter:', newUrl);
          history.pushState({}, '', newUrl);
        }
      } else {
        console.warn('⚠️ App task details not found for:', taskId);
      }
    };
    
    // Override global toggleTaskDetails to use app-specific version when on app page
    const originalToggleTaskDetails = window.toggleTaskDetails;
    window.toggleTaskDetails = (taskId) => {
      if (window.appTabbedManager && window.location.pathname.includes('/app')) {
        // Use app-specific version
        window.toggleAppTaskDetails(taskId);
      } else {
        // Use original version for main tasks page
        originalToggleTaskDetails(taskId);
      }
    };
  }

  // Load app backups
  async loadAppBackups() {
    const backupAppNameElement = document.getElementById('backup-app-name');
    if (backupAppNameElement) {
      const formattedAppName = this.currentApp
        ? (window.getAppDisplayName ? window.getAppDisplayName(this.currentApp) : (this.currentApp.charAt(0).toUpperCase() + this.currentApp.slice(1)))
        : 'Unknown App';
      backupAppNameElement.textContent = formattedAppName;
    }

    if (!this.currentApp || typeof BackupAppCard === 'undefined') {
      const status = document.getElementById('backup-app-card-status');
      if (status) status.textContent = 'No app selected.';
      return;
    }

    if (!this.backupAppCard || this.backupAppCard.appName !== this.currentApp) {
      this.backupAppCard = new BackupAppCard(this.currentApp);
      window.backupAppCard = this.backupAppCard;
    }
    await this.backupAppCard.render();
  }

  // Initialize the tabbed manager
  async initialize() {
    // Prevent double initialization
    if (this.initialized) {
      // console.log('⚠️ AppTabbedManager already initialized, skipping');
      return;
    }
    
    // console.log('🚀 AppTabbedManager initializing, currentApp:', this.currentApp);
    
    // Initialize task system if not already done (with retry)
    if (this.tasksManager && !this.tasksManager.commands) {
      let initialized = false;
      let attempts = 0;
      const maxAttempts = 5;
      
      while (!initialized && attempts < maxAttempts) {
        // console.log(`🔄 Attempting to initialize task system (${attempts + 1}/${maxAttempts})...`);
        try {
          initialized = this.tasksManager.initializeTaskSystem();
          if (initialized) {
            // console.log('✅ Task system initialized successfully');
          }
        } catch (error) {
          console.error('❌ Task system initialization error:', error);
        }
        
        if (!initialized) {
          attempts++;
          await new Promise(resolve => setTimeout(resolve, 200)); // Wait 200ms
        }
      }
      
      if (!initialized) {
        console.warn('⚠️ Task system initialization failed after retries');
      }
    }
    
    // Stale .task-running from a prior session won't survive a reload, but the
    // DOM might still carry it from a re-render — clear it up front.
    this.enableTabs();
    document.querySelectorAll('button.task-running, .tab-button.task-running, .main-tab-button.task-running')
      .forEach(button => this.restoreButton(button));

    // SSE drives task lifecycle events; the reconcile pass below is a
    // safety net for the cases where they don't reach us — bus disconnect,
    // missed event during reconnect, throttled background tab. Several
    // triggers so a stuck "running" state self-heals quickly:
    //   * a faster periodic tick (5s) instead of the previous 30s
    //   * whenever the SSE bus reconnects (`taskBusReady`)
    //   * whenever the page becomes visible again (`visibilitychange`)
    //   * whenever the window regains focus
    if (!this._watchdogStarted) {
      this._watchdogStarted = true;
      const reconcile = () => this.reconcileRunningTasks().catch(() => {});
      // Adaptive cadence: when a task is actively running, poll every
      // 1.5s so a missed SSE event surfaces quickly. When idle, fall back
      // to 5s. Net: worst-case lag drops from ~10s to ~1.5s while keeping
      // background load minimal.
      const tick = () => {
        reconcile();
        const next = (this.runningTasks && this.runningTasks.size > 0) ? 1500 : 5000;
        clearTimeout(this._reconcileTimer);
        this._reconcileTimer = setTimeout(tick, next);
      };
      this._reconcileTimer = setTimeout(tick, 1500);
      window.addEventListener('taskBusReady', reconcile);
      window.addEventListener('focus', reconcile);
      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'visible') reconcile();
      });
    }

    // Set current app from URL BEFORE setting up URL monitoring
    const urlAppName = this.getAppFromURL();
    // console.log('🔍 Setting initial currentApp from URL:', urlAppName);
    this.currentApp = urlAppName;
    
    // Check for running tasks for this app and auto-switch to tasks tab if found
    if (this.currentApp) {
      const running = this.getRunningTaskForApp(this.currentApp);
      if (running) {
        this.switchTab('tasks');
        this.disableTabs();
        this.activeTaskId = running.taskId;
      }
    }
    
    // Check for task parameter and handle it AFTER tasks are loaded
    // Use original URL since the current URL might have been modified
    const urlParams = new URLSearchParams(this.originalSearch);
    // console.log('🔍 Original URL search during init:', this.originalSearch);
    // console.log('🔍 Original URL params during init:', Object.fromEntries(urlParams.entries()));
    let taskId = urlParams.get('task');
    // console.log('🔍 Task ID from original params:', taskId);
    
    // Fallback: Check sessionStorage if URL doesn't have task parameter
    if (!taskId) {
      taskId = sessionStorage.getItem('pendingTaskId');
      // console.log('🔍 Task ID from sessionStorage fallback:', taskId);
      // Clear sessionStorage after using it
      if (taskId) {
        sessionStorage.removeItem('pendingTaskId');
      }
    }
    if (taskId) {
      // console.log('🔍 Task parameter found:', taskId);
      // Store the task ID to handle after tasks are loaded
      this.pendingTaskId = taskId;
      // Force tasks tab
      this.switchTab('tasks');
    }
    
    // Monitor URL changes for app navigation
    this.setupURLMonitoring();
    
    // Listen for task creation events
    this.setupTaskEventListeners();
    
    // Set initial active tab (only if no task parameter)
    if (!taskId) {
      const initialTab = this.getTabFromURL();
      // console.log('🔄 Setting initial tab:', initialTab, 'with currentApp:', this.currentApp);
      this.switchTab(initialTab);
    }
    
    // Set global reference for other components
    window.appTabbedManager = this;
  }

  // Monitor URL changes for app navigation
  setupURLMonitoring() {
    // Listen for popstate events (browser back/forward)
    window.addEventListener('popstate', () => {
      if (!this.isAppPage()) return; // Only monitor on app pages
      
      const newAppName = this.getAppFromURL();
      // Only update if currentApp is already set and app actually changed
      if (this.currentApp && newAppName !== this.currentApp) {
        // console.log('🔄 URL changed, updating app from', this.currentApp, 'to', newAppName);
        this.updateApp(newAppName);
      }
    });
  }

  // Watch for new tasks and switch to logs tab
  watchForTaskCreation() {
    // Auto-switch to Tasks tab when a fresh task appears for the current app.
    setInterval(async () => {
      if (this.currentApp && this.currentTab !== 'tasks') {
        // Skip if a recent uninstall asked us not to auto-switch.
        const until = this._suppressTaskAutoSwitch?.get(this.currentApp);
        if (until && Date.now() < until) return;
        if (until) this._suppressTaskAutoSwitch.delete(this.currentApp);
        try {
          await this.tasksManager.loadTasks();
          const allTasks = this.tasksManager.tasks || [];
          // Only switch on RUNNING/QUEUED tasks created recently — completed
          // ones don't need watching, and would otherwise bounce the user
          // back to Tasks right after they've been switched away.
          const recentTasks = allTasks.filter(task =>
            task.app === this.currentApp &&
            (task.status === 'running' || task.status === 'queued' || task.status === 'pending') &&
            new Date(task.createdAt) > new Date(Date.now() - 5000)
          );
          if (recentTasks.length > 0) this.switchTab('tasks');
        } catch (error) {
          console.error('Error watching for tasks:', error);
        }
      }
    }, 5000);
  }

  // Create backup (placeholder function)
  async createBackup(appName) {
    // Placeholder - will be implemented with actual backup logic
    // console.log(`Creating backup for ${appName}...`);
  }

  // Setup task event listeners for button state management
  setupTaskEventListeners() {
    window.addEventListener('taskCreated', (event) => {
      const { taskId, appName, action } = event.detail;
      const key = this.taskKey(appName, action);

      // console.log('📌 taskCreated: appName=%s, currentApp=%s, action=%s, key=%s',
      //   appName, this.currentApp, action, key);

      if (this.runningTasks.has(key)) {
        const existing = this.runningTasks.get(key);
        // Same task firing twice — `createAndExecuteTask` dispatches taskCreated
        // synchronously, and the SSE bus also dispatches it when the new task
        // file shows up. Both events carry the same taskId; treat as a no-op.
        if (existing && existing.taskId === taskId) return;

        if (window.notificationSystem) {
          // Match the per-task-type icon used everywhere else (install ✅,
          // backup 💾, etc.) so the user sees *what kind* of task is in
          // progress, not just a generic warning triangle.
          const typeIcon = (window.tasksManager && window.tasksManager.getTaskTypeIcon
            ? window.tasksManager.getTaskTypeIcon({ type: action })
            : null)?.icon || '';
          const customIcon = typeIcon ? `<span style="font-size:18px;line-height:1;">${typeIcon}</span>` : null;
          window.notificationSystem.show(
            `<strong>Task Already Running</strong><br>
             <small>A ${action} task for ${appName} is already in progress.</small><br>
             <small>Please wait for the current task to complete.</small>`,
            'warning',
            null,
            null,
            null,
            customIcon
          );
        }
        return;
      }

      this.runningTasks.set(key, { taskId, appName, action });
      // console.log('📌 taskCreated: stored in runningTasks, will disable=%s', appName === this.currentApp);

      if (appName === this.currentApp) {
        this.disableAppButtons(appName, action);
        this.activeTaskId = taskId;
      }
    });

    window.addEventListener('taskCompleted', (event) => {
      const { taskId, appName, action } = event.detail;
      const key = this.taskKey(appName, action);

      // Primary path: delete by exact (app, action) key.
      this.runningTasks.delete(key);
      // Belt-and-braces: also remove any entry that matches the taskId. If
      // `action` ever differs between the original `taskCreated` and this
      // `taskCompleted` event (different code paths produce slightly
      // different action strings), the key-based delete above is a no-op
      // and the row would stay "running" forever. Match on id too.
      for (const [k, info] of this.runningTasks) {
        if (info && info.taskId === taskId) this.runningTasks.delete(k);
      }

      const stillRunning = this.getRunningTaskForApp(appName);
      if (stillRunning) {
        if (appName === this.currentApp) this.activeTaskId = stillRunning.taskId;
        return;
      }

      // Always run the DOM cleanup; enableAppButtons is idempotent.
      this.enableAppButtons(appName);
      if (appName === this.currentApp) this.activeTaskId = null;

      if ((action === 'backup' || action === 'delete' || action === 'delete_all') && this.backupAppCard) {
        this.backupAppCard.render();
      }
    });

    // Extra safety net: any `taskUpdated` whose status is terminal should
    // also clear our local tracking. The bus normally dispatches a
    // dedicated `taskCompleted` instead — but if a single task file write
    // jumps a status straight from queued/pending to completed in a way
    // that the bus classifies as "updated, !isNew", we'd miss it otherwise.
    window.addEventListener('taskUpdated', (event) => {
      const t = event.detail && event.detail.task;
      if (!t || !t.id) return;
      const terminal = t.status === 'completed' || t.status === 'failed' || t.status === 'cancelled';
      if (!terminal) return;
      let removed = false;
      for (const [k, info] of this.runningTasks) {
        if (info && info.taskId === t.id) { this.runningTasks.delete(k); removed = true; }
      }
      if (!removed) return;
      const appName = t.app || null;
      if (appName && !this.getRunningTaskForApp(appName)) {
        this.enableAppButtons(appName);
        if (appName === this.currentApp) this.activeTaskId = null;
      }
    });
  }
  
  // Helper method to get action for a task (used for duplicate detection)
  getActionForTask(taskId) {
    // Prefer our own runningTasks map — it knows the action by source of truth.
    for (const info of this.runningTasks.values()) {
      if (info.taskId === taskId) return info.action;
    }
    if (this.tasksManager && this.tasksManager.tasks) {
      const task = this.tasksManager.tasks.find(t => t.id === taskId);
      return task ? task.type : 'unknown';
    }
    return 'unknown';
  }

  // Disable config, services and backup tabs when task is running
  disableTabs() {
    const tabs = ['config', 'services', 'tools', 'backups']
      .map(name => document.querySelector(`.main-tab-button[data-tab="${name}"], .tab-button[data-tab="${name}"]`))
      .filter(Boolean);

    for (const tab of tabs) {
      tab.disabled = true;
      tab.classList.add('disabled', 'task-running');
      tab.style.opacity = '0.5';
      tab.style.pointerEvents = 'none';
      tab.title = 'Disabled due to task running';
    }
  }

  // Enable config, services and backup tabs when task completes
  enableTabs() {
    const tabs = ['config', 'services', 'tools', 'backups']
      .map(name => document.querySelector(`.main-tab-button[data-tab="${name}"], .tab-button[data-tab="${name}"]`))
      .filter(Boolean);

    for (const tab of tabs) {
      tab.disabled = false;
      tab.classList.remove('disabled', 'task-running');
      tab.style.opacity = '';
      tab.style.pointerEvents = '';
      tab.title = '';
    }
  }

  // Disable app buttons during task execution
  disableAppButtons(appName, action) {
    // console.log('🚫 disableAppButtons called: appName=%s, action=%s, currentApp=%s',
    //   appName, action, this.currentApp);

    // Also disable config and backup tabs
    this.disableTabs();

    // Find ALL action buttons in the app content section (config, backup, etc.)
    // This includes install, uninstall, update, backup, and any other action buttons
    const appContent = document.querySelector('.app-content, .config-section, .backup-section, .tab-content');
    if (!appContent) {
      console.warn('⚠️ App content section not found');
      return;
    }

    // Disable ALL buttons in the app content section
    const allButtons = appContent.querySelectorAll('button:not([disabled]):not(.tab-button)');
    // console.log('🚫 disableAppButtons found %d buttons to disable', allButtons.length);
    
    allButtons.forEach(button => {
      // Skip tab buttons (config, backup, tasks tabs)
      if (button.classList.contains('tab-button')) {
        return;
      }
      // Logs toggle buttons stay clickable while a task runs — viewing
      // log output is read-only and the whole point during a long task.
      if (button.dataset.action === 'toggle-logs' ||
          button.classList.contains('service-logs') ||
          button.classList.contains('toggle-details')) {
        return;
      }

      // Hide download buttons permanently as they're not needed
      if (button.textContent && button.textContent.toLowerCase().includes('download')) {
        button.style.display = 'none';
        return;
      }

      button.disabled = true;
      button.classList.add('disabled', 'task-running');
      
      // Add loading spinner to buttons that don't already have one
      if (!button.querySelector('.spinner')) {
        const originalContent = button.innerHTML;
        button.dataset.originalContent = originalContent;
        
        // Replace entire content with spinner + text (no icons)
        // Extract text content from original HTML (remove icons/SVGs)
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = originalContent;
        const textContent = tempDiv.textContent || tempDiv.innerText || originalContent;
        
        // console.log('🔃 Adding spinner to button:', button.textContent.trim(), 'for app:', appName);
      button.innerHTML = `
          <span class="spinner" style="
            display: inline-block;
            width: 14px;
            height: 14px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-top: 2px solid #ffffff;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-right: 0px;
            vertical-align: middle;
          "></span>
          <span style="vertical-align: middle;">${textContent.trim()}</span>
        `;
      }
    });
    
    // Track which buttons were disabled
    allButtons.forEach(button => {
      this.disabledButtons.add(button);
    });
    
    // console.log(`🔍 Disabled ${allButtons.length} buttons for ${appName} during ${action}`);
  }

  // Restore button state when switching tabs. Only disable if the *current* app
  // has a running task — without this check, switching tabs on app B while a task
  // is running for app A would disable app B's buttons and tabs.
  restoreButtonState() {
    const running = this.getRunningTaskForApp(this.currentApp);
    if (running) {
      this.activeTaskId = running.taskId;
      this.disableAppButtons(this.currentApp, running.action);
    } else {
      this.enableTabs();
      this.enableAppButtons(this.currentApp);
    }
  }

  // SSE safety-net. The bus normally delivers terminal transitions in
  // milliseconds; this re-syncs from the API if the bus has been disconnected.
  async reconcileRunningTasks() {
    if (this.runningTasks.size === 0) {
      // Nothing tracked but the DOM still says disabled — that's a stale
      // leftover from an earlier run whose `taskCompleted` we missed. Just
      // force-enable so the user isn't stuck.
      const configTab = document.querySelector('.main-tab-button[data-tab="config"], .tab-button[data-tab="config"]');
      if (configTab && (configTab.classList.contains('task-running') || configTab.disabled)) {
        this.enableTabs();
        if (this.currentApp) this.enableAppButtons(this.currentApp);
      }
      return;
    }
    for (const info of Array.from(this.runningTasks.values())) {
      let task = null;
      if (window.taskEventBus) task = window.taskEventBus.getTask(info.taskId);
      if (!task) {
        try {
          const res = await fetch(`/api/tasks/${info.taskId}`);
          if (res.ok) task = await res.json();
          else if (res.status === 404) {
            // Task file gone — fire one synthetic completion + stop polling
            // so we don't loop forever on a deleted task.
            this.runningTasks.delete(info.taskId);
            window.dispatchEvent(new CustomEvent('taskCompleted', {
              detail: { taskId: info.taskId, appName: info.appName, action: info.action, status: 'completed', task: null, timestamp: Date.now() }
            }));
            continue;
          }
        } catch { continue; }
      }
      if (!task) continue;
      if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
        window.dispatchEvent(new CustomEvent('taskCompleted', {
          detail: { taskId: task.id, appName: info.appName, action: info.action, status: task.status, task, timestamp: Date.now() }
        }));
      }
    }
  }

  restoreButton(button) {
    if (!button) return;
    button.disabled = false;
    button.classList.remove('disabled', 'task-running');
    if (button.dataset.originalContent) {
      button.innerHTML = button.dataset.originalContent;
      delete button.dataset.originalContent;
    }
    button.querySelectorAll('.spinner').forEach(s => s.remove());
  }

  enableAppButtons(appName) {
    this.enableTabs();

    this.disabledButtons.forEach(button => this.restoreButton(button));
    this.disabledButtons.clear();

    document.querySelectorAll('button.task-running, .tab-button.task-running, .main-tab-button.task-running')
      .forEach(button => this.restoreButton(button));

    const appContent = document.querySelector('.app-content, .config-section, .backup-section, .tab-content');
    if (appContent) {
      appContent.querySelectorAll('button.disabled, button[disabled]:not(.tab-button)')
        .forEach(button => this.restoreButton(button));
    }
  }
}

// Export for use
window.AppTabbedManager = AppTabbedManager;

// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', async () => {
  // console.log('🔍 DOMContentLoaded: Skipping automatic initialization - SPA will handle it');
  // Don't initialize here - let SPA handle it
});

// Also initialize when scripts are loaded (for SPA navigation)
window.addEventListener('load', async () => {
  // console.log('🔍 Window load: Skipping automatic initialization - SPA will handle it');
  // Don't initialize here - let SPA handle it
});
