// Auto-extracted from tasks-manager.js (verbatim) — augments TasksManager.prototype. Loaded after the base.
Object.assign(TasksManager.prototype, {
  renderTasks() {
    const container = document.getElementById('tasks-list');
    if (!container) return;

    // Capture which task panels are currently open and where the user
    // is scrolled to *before* the innerHTML rebuild. Without this,
    // every refresh slams every expanded log shut and snaps to the top
    // — which makes watching a live task feel hostile.
    const expandedIds = new Set();
    container.querySelectorAll('.task-details.task-details-open').forEach(el => {
        const taskId = (el.id || '').replace(/^details-/, '');
        if (taskId) expandedIds.add(taskId);
    });
    const scrollParent = container.closest('.main') || document.scrollingElement || document.documentElement;
    const savedScrollTop = scrollParent ? scrollParent.scrollTop : window.scrollY;

    // Filter tasks based on current category and specific task
    let filteredTasks = this.filterTasksByCategory(this.tasks, this.currentCategory);
    
    // Note: Don't filter out other tasks when one is highlighted
    // highlightedTaskId is only for auto-expansion, not for filtering

    if (filteredTasks.length === 0) {
      let message;
      if (this.highlightedTaskId) {
        message = `Task ${this.highlightedTaskId} not found`;
      } else {
        // Category display names sometimes already end in "Tasks" (e.g.
        // "All Tasks", "Running Tasks") — naive interpolation produced
        // "No all tasks tasks found". Strip the trailing word when
        // present, and special-case the "all" bucket to read naturally.
        const catLow = this.getCategoryDisplayName(this.currentCategory).toLowerCase();
        if (catLow === 'all tasks' || catLow === 'all') {
          message = 'No tasks found';
        } else if (catLow.endsWith(' tasks')) {
          message = `No ${catLow} found`;
        } else {
          message = `No ${catLow} tasks found`;
        }
      }
      
      container.innerHTML = `
        <div class="task-item">
          <div class="task-header">
            <div class="task-info">
              <span class="task-status status-completed">info</span>
              <span class="task-command">$ ${message}</span>
              <span class="task-time">just now</span>
            </div>
            <div class="task-actions">
            </div>
          </div>
          
          <div class="task-details task-details-open">
            <div class="task-output">Run a task or install an application to see your task list here</div>
          </div>
        </div>
      `;
      return;
    }

    // Sort tasks by creation time (newest first)
    const sortedTasks = filteredTasks.sort((a, b) => 
      new Date(b.createdAt) - new Date(a.createdAt)
    );

    const html = sortedTasks.map(task => this.renderTask(task)).join('');
    container.innerHTML = html;

    // Re-open everything that was open before the rebuild and re-attach
    // log streaming so live tasks keep updating without an extra click.
    expandedIds.forEach(taskId => {
        const details = document.getElementById(`details-${taskId}`);
        if (!details) return;
        details.style.display = 'block';
        details.classList.add('task-details-open');
        const btn = document.querySelector(`.task-btn.toggle-details[onclick*="toggleTaskDetails('${taskId}')"]`);
        if (btn) btn.classList.add('expanded');

        const t = this.tasks.find(x => x.id === taskId);
        if (t && (t.status === 'running' || t.status === 'queued' || t.status === 'pending')) {
            if (typeof this.startLogStreaming === 'function') this.startLogStreaming(taskId, t);
        } else {
            if (typeof this.loadTaskLogs === 'function') this.loadTaskLogs(taskId);
        }
    });

    // Restore scroll. Defer one frame so the browser has laid out the
    // new content height before we scroll back into it.
    if (scrollParent) {
        requestAnimationFrame(() => { scrollParent.scrollTop = savedScrollTop; });
    }

    // Resync the multi-select UI after the render. The Clear All label
    // + master-checkbox tri-state need to reflect the just-rendered row
    // set (selections can become stale across category switches).
    if (typeof this._updateSelectionUI === 'function') this._updateSelectionUI();
  },
  filterTasksByCategory(tasks, category) {
    switch (category) {
      case 'all':
        return tasks;
      
      case 'queued':
      case 'running':
      case 'completed':
      case 'failed':
        return tasks.filter(task => task.status === category);
      
      case 'install':
      case 'uninstall':
        return tasks.filter(task => task.type === category);
      
      case 'management':
        return tasks.filter(task => 
          ['restart', 'start', 'stop'].includes(task.type)
        );
      
      case 'backup':
        return tasks.filter(task => 
          ['backup', 'restore', 'delete'].includes(task.type)
        );
      
      case 'config':
        return tasks.filter(task => task.type === 'update_config');
      
      default:
        // Assume it's an app name
        return tasks.filter(task => task.app === category);
    }
  },
  getCategoryDisplayName(category) {
    const displayNames = {
      'all': 'All Tasks',
      'queued': 'Queued',
      'running': 'Running',
      'completed': 'Completed',
      'failed': 'Failed',
      'install': 'Install',
      'uninstall': 'Uninstall',
      'management': 'Management',
      'backup': 'Backups',
      'config': 'Configuration',
      'libreportal': 'LibrePortal'
    };
    
    return displayNames[category] || category.charAt(0).toUpperCase() + category.slice(1);
  },
  renderTask(task) {
    
    // Debug undefined status
    if (!task.status) {
      console.warn(`⚠️ Task ${task.id} has undefined status:`, task);
    }
    
    const statusClass = `status-${task.status || 'unknown'}`;
    const timeAgo = this.getTimeAgo(task.createdAt);
    const isRunning = task.status === 'running';
    const isFailed = task.status === 'failed';
    const hasOutput = task.output && task.output.length > 0;
    const hasError = task.error && task.error.length > 0;
    const hasLogs = task.log && Array.isArray(task.log) && task.log.length > 0;
    
    
    const executionTime = task.startedAt && task.completedAt ? 
      this.calculateExecutionTime(task.startedAt, task.completedAt) : null;
    

    return `
      <div class="task-item" data-task-id="${task.id}">
        <div class="task-header" onclick="toggleTaskDetails('${task.id}')">
          <div class="task-info">
            ${this.renderTaskIcons(task)}
            <span class="task-title">${this.formatCommandForUser(task)}</span>
            <span class="task-status ${statusClass}">${this.getStatusIcon(task.status)} ${task.status ? task.status.toUpperCase() : 'UNKNOWN'}</span>
            <span class="task-time">${timeAgo}</span>
            ${executionTime ? `<span class="task-duration">⏱️ ${executionTime}</span>` : ''}
          </div>
          <div class="task-actions">
            ${isFailed ? `
              <button class="task-btn retry" onclick="event.stopPropagation(); retryTask('${task.id}')" title="Retry Task">
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
                </svg>
                Retry
              </button>
            ` : ''}
            <button class="task-btn toggle-details" onclick="event.stopPropagation(); toggleTaskDetails('${task.id}')" title="Toggle Task Details">
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <polyline points="6,9 12,15 18,9"></polyline>
              </svg>
              <span class="task-btn-label">Logs</span>
            </button>
            <button class="task-btn delete" onclick="event.stopPropagation(); deleteTask('${task.id}')" title="Delete Task">
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <line x1="18" y1="6" x2="6" y2="18"></line>
                <line x1="6" y1="6" x2="18" y2="18"></line>
              </svg>
              <span class="task-btn-label">Delete</span>
            </button>
            <label class="task-select" onclick="event.stopPropagation();" title="Select for bulk delete">
              <input type="checkbox" data-task-select="${task.id}" ${this.selectedTaskIds.has(task.id) ? 'checked' : ''}
                     onchange="event.stopPropagation(); window.tasksManager && tasksManager.toggleTaskSelection('${task.id}', this.checked)">
              <span class="task-select-box" aria-hidden="true"></span>
            </label>
          </div>
        </div>
        
        <!-- Simplified details section (metadata and logs only) -->
        <div class="task-details" id="details-${task.id}">
          <!-- Task metadata -->
          <div class="task-meta">
            <div class="meta-item">
              <strong>Task ID:</strong> <a href="${window.taskPath ? window.taskPath('all', task.id) : '/tasks/all'}" class="task-id-link" data-task-id="${task.id}">${task.id}</a>
            </div>
            <div class="meta-item">
              <strong>Type:</strong> ${task.type || 'unknown'}
            </div>
            <div class="meta-item">
              <strong>App:</strong> ${task.app ? `<a href="/app/${task.app}" class="task-app-link" data-app-name="${task.app}">${task.app}</a>` : 'system'}
            </div>
            <div class="meta-item">
              <strong>Created:</strong> ${new Date(task.createdAt).toLocaleString()}
            </div>
            ${task.startedAt ? `
              <div class="meta-item">
                <strong>Started:</strong> ${new Date(task.startedAt).toLocaleString()}
              </div>
            ` : ''}
            ${task.completedAt ? `
              <div class="meta-item">
                <strong>Completed:</strong> ${new Date(task.completedAt).toLocaleString()}
              </div>
            ` : ''}
            ${executionTime ? `
              <div class="meta-item">
                <strong>Execution Time:</strong> ${executionTime}
              </div>
            ` : ''}
          </div>
          
          <!-- Logs section only -->
          <div class="task-logs">
            <div class="log-container terminal-style" id="logs-${task.id}" style="height: 200px; overflow-y: auto; position: relative;">
              ${hasLogs ?
                task.log.map(log => `<div class="log-entry">${this.taskManager.parseAnsiColors(log)}</div>`).join('') :
                '<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(10, 18, 36, 0.85); display: flex; align-items: center; justify-content: center; z-index: 10;"><div class="loading-spinner" style="width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.18); border-top: 2px solid #fff; border-radius: 50%; animation: spin 1s linear infinite; margin-right: 8px;"></div>Loading logs...</div></div>'
              }
            </div>
          </div>
        </div>
      </div>
    `;
  },
  renderTaskIcons(task) {
    const typeIcon = `<span class="task-type-icon">${this.getTaskTypeIcon(task).icon}</span>`;
    // `app: 'system'` and `app: 'updater'` are category sentinels (config_update,
    // system_update, updater_check/apply_all, …), not real app slugs, so they have
    // no /core/icons/apps/<x>.svg — fall through to the LibrePortal-system branch
    // so those tasks still get a logo. (updater_apply/rollback carry a real app
    // slug and keep their own app icon.)
    const isSystemSentinel = task.app === 'system' || task.app === 'updater';
    if (task.app && !isSystemSentinel) {
      const appIconPath = this.getAppIconPath(task);
      return `${typeIcon}<img src="${appIconPath}" alt="${task.app}" class="task-app-icon" onerror="this.style.display='none'">`;
    }
    if (isSystemSentinel || this.isLibrePortalSystemTask(task)) {
      return `${typeIcon}<img src="/core/icons/libreportal.svg" alt="LibrePortal" class="task-app-icon">`;
    }
    return typeIcon;
  },
  getAppIconPath(task) {
    if (!task.app) return null;
    
    // Try to get icon from commands if available
    if (this.commands && this.commands.getAppData) {
      const appData = this.commands.getAppData(task.app);
      if (appData && appData.icon) {
        return appData.icon;
      }
    }
    
    // Default icon path
    return `/core/icons/apps/${task.app}.svg`;
  },
  isLibrePortalSystemTask(task) {
    if (!task || !task.command || task.app) return false;
    return /^libreportal (setup|backup\s+all|backup\s+system|backup\s+verify|backup\s+location|backup\s+repo|restore\s+migrate\s+system|restore\s+migrate\s+discover|restore\s+first-run|webui|config|update|verify|system\s+(reclaim|image))\b/.test(task.command);
  },
  getTaskTypeIcon(task) {
    if (!task.type) return { icon: '⚙️', class: 'custom' };

    const iconMap = {
      'install': { icon: '✅', class: 'install' },
      'app-install': { icon: '✅', class: 'install' },
      'uninstall': { icon: '❌', class: 'uninstall' },
      'restart': { icon: '🔄', class: 'restart' },
      'start': { icon: '▶️', class: 'start' },
      'stop': { icon: '⏹️', class: 'stop' },
      'update': { icon: '⬆️', class: 'update' },
      'rebuild': { icon: '🔨', class: 'rebuild' },
      'backup': { icon: '💾', class: 'backup' },
      'restore': { icon: '📦', class: 'restore' },
      'delete': { icon: '🗑️', class: 'delete' },
      'delete_all': { icon: '🗑️', class: 'delete' },
      'system_image_rm': { icon: '🗑️', class: 'delete' },
      'verify': { icon: '🛡️', class: 'verify' },
      'setup-config': { icon: '🛠️', class: 'setup' },
      'setup-finalize': { icon: '🎉', class: 'setup' },
      'updater_check': { icon: '🔍', class: 'verify' },
      'updater_apply': { icon: '⬆️', class: 'update' },
      'updater_apply_all': { icon: '⬆️', class: 'update' },
      'updater_rollback': { icon: '↩️', class: 'restore' },
      'artifact_apply': { icon: '⚡', class: 'update' },
      'artifact_revert': { icon: '↩️', class: 'restore' },
      'custom': { icon: '⚙️', class: 'custom' }
    };

    return iconMap[task.type] || iconMap['custom'];
  },
  getStatusIcon(status) {
    const icons = {
      queued: '⏳',
      running: '🔄',
      completed: '✅',
      failed: '❌'
    };
    return icons[status] || '📋';
  },
  updateStats() {
    const stats = {
      queued: this.tasks.filter(t => t.status === 'queued').length,
      running: this.tasks.filter(t => t.status === 'running').length,
      completed: this.tasks.filter(t => t.status === 'completed').length,
      failed: this.tasks.filter(t => t.status === 'failed').length
    };

    Object.keys(stats).forEach(status => {
      const element = document.getElementById(`${status}-count`);
      if (element) element.textContent = stats[status];
    });
  },
  updateSidebarCounts() {
    // Update all sidebar category counts
    const categories = ['all', 'queued', 'running', 'completed', 'failed', 'install', 'uninstall', 'management', 'backup', 'config'];
    
    categories.forEach(category => {
      const count = this.filterTasksByCategory(this.tasks, category).length;
      const element = document.getElementById(`count-${category}`);
      if (element) element.textContent = count;
    });

    // Update app-specific counts
    const apps = [...new Set(this.tasks.map(task => task.app).filter(Boolean))];
    apps.forEach(app => {
      const count = this.tasks.filter(task => task.app === app).length;
      const element = document.getElementById(`count-app-${app}`);
      if (element) element.textContent = count;
    });
  },
  async generateAppCategories() {
    const container = document.getElementById('app-categories');
    if (!container) return;

    try {
      // Show loading state
      container.innerHTML = '<div class="loading-categories"><div class="loading-spinner"></div><p style="color: #ffffff;">Loading apps...</p></div>';

      // Try to load apps data if not already available
      let appsData = window.apps || [];
      
      // If window.apps is not available, try to load it
      if (!appsData || appsData.length === 0) {
        try {
          const appsResponse = await fetch('/read-file?path=apps.json');
          if (appsResponse.ok) {
            const appsText = await appsResponse.text();
            if (appsText.trim()) {
              appsData = JSON.parse(appsText);
              window.apps = appsData; // Cache for future use
            }
          }
        } catch (error) {
        }
      }
      
      // Filter for installed apps only and extract slugs
      const installedApps = appsData
        .filter(app => app.installed === true || app.status === 'installed')
        .map(app => {
          // Extract slug from command like "libreportal app install adguard"
          const command = app.command || '';
          const slugMatch = command.match(/libreportal app install\s+(.+)$/);
          const slug = slugMatch ? slugMatch[1].trim() : '';
          
          // Extract title from "Title - Description" format
          const fullName = app.name || '';
          const title = fullName.split(' - ')[0].trim();
          
          return { slug, title };
        })
        .filter(app => app.slug && app.title);
      
      // If no installed apps found from apps data, fall back to task-based apps
      if (installedApps.length === 0) {
        const taskApps = [...new Set(this.tasks.map(task => task.app).filter(Boolean))];
        
        if (taskApps.length === 0) {
          container.innerHTML = '<div class="no-apps" style="color: var(--text-secondary); font-size: 12px; padding: 8px;">No apps found</div>';
          return;
        }

        const appCategories = taskApps.map(app => {
          const taskCount = this.tasks.filter(task => task.app === app).length;
          const displayName = window.getAppDisplayName ? window.getAppDisplayName(app) : (app.charAt(0).toUpperCase() + app.slice(1));
          return `
            <a href="#" class="sidebar-item" data-category="${app}" onclick="filterTasksByCategory('${app}')">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
                <line x1="9" y1="9" x2="15" y2="9"></line>
                <line x1="9" y1="15" x2="15" y2="15"></line>
              </svg>
              ${displayName}
              <span class="task-count" id="count-app-${app}">${taskCount}</span>
            </a>
          `;
        }).join('');

        container.innerHTML = appCategories;
        return;
      }

      const appCategories = installedApps.map(app => {
        const taskCount = this.tasks.filter(task => task.app === app.slug).length;
        return `
          <a href="#" class="sidebar-item" data-category="${app.slug}" onclick="filterTasksByCategory('${app.slug}')">
            <div class="status-indicator status-installed"></div>
            ${app.title}
            <span class="task-count" id="count-app-${app.slug}">${taskCount}</span>
          </a>
        `;
      }).join('');

      container.innerHTML = appCategories;
    } catch (error) {
      console.error('Error generating app categories:', error);
      // Final fallback to task-based app list
      const apps = [...new Set(this.tasks.map(task => task.app).filter(Boolean))];
      
      if (apps.length === 0) {
        container.innerHTML = '<div class="no-apps" style="color: var(--text-secondary); font-size: 12px; padding: 8px;">No apps found</div>';
        return;
      }

      const appCategories = apps.map(app => {
        const taskCount = this.tasks.filter(task => task.app === app).length;
        const displayName = window.getAppDisplayName ? window.getAppDisplayName(app) : (app.charAt(0).toUpperCase() + app.slice(1));
        return `
          <a href="#" class="sidebar-item" data-category="${app}" onclick="filterTasksByCategory('${app}')">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
              <line x1="9" y1="9" x2="15" y2="9"></line>
              <line x1="9" y1="15" x2="15" y2="15"></line>
            </svg>
            ${displayName}
            <span class="task-count" id="count-app-${app}">${taskCount}</span>
          </a>
        `;
      }).join('');

      container.innerHTML = appCategories;
    }
  },
  filterTasksByCategoryHandler(category) {
    this.currentCategory = category;

    // Clear specific task filter when switching categories. Picking a
    // category is the user steering the view — auto-follow stands down too.
    this.highlightedTaskId = null;
    this.followRunning = false;
    
    // Update URL
    this.updateURL(category);
    
    // Update sidebar active state
    document.querySelectorAll('.sidebar-item').forEach(item => {
      item.classList.toggle('active', item.dataset.category === category);
    });
    
    this.renderTasks();
  },
  showError(message) {
    const container = document.getElementById('tasks-list');
    if (container) {
      container.innerHTML = `
        <div class="terminal-error">
          <div class="error-text">$ ${message}</div>
          <div class="error-cursor">_</div>
        </div>
      `;
    }
  },
});
