// Auto-extracted from tasks-manager.js (verbatim) — augments TasksManager.prototype. Loaded after the base.
Object.assign(TasksManager.prototype, {
  // SSE-driven log streaming. Subscribes to `taskLog` events from the bus and
  // appends incoming chunks. Initial backlog is fetched once via the API.
  //
  // Resilient to DOM replacement: the logs container can be wiped out by
  // `renderTasks()` (the 30s auto-refresh) or by `loadTaskLogs()` (toggle
  // re-open). Instead of capturing a `preElement` reference once, `render()`
  // re-locates / re-creates it on every call from the cumulative `buffered`
  // string. Idempotent: if already streaming, a second call just re-renders.
  async startLogStreaming(taskId, task) {
    if (!this.activeLogStreams) this.activeLogStreams = new Map();

    if (this.activeLogStreams.has(taskId)) {
      const existing = this.activeLogStreams.get(taskId);
      if (existing && typeof existing.render === 'function') existing.render();
      return;
    }

    const state = { buffered: '' };

    const render = () => {
      const logsContainer = document.getElementById(`logs-${taskId}`);
      if (!logsContainer) return;
      const overlay = logsContainer.querySelector('div[style*="position: absolute"]');
      if (overlay) overlay.remove();
      let preElement = logsContainer.querySelector('pre.output-content');
      if (!preElement) {
        logsContainer.innerHTML = '';
        preElement = document.createElement('pre');
        preElement.className = 'output-content terminal-style';
        logsContainer.appendChild(preElement);
      }
      const atBottom = logsContainer.scrollHeight - logsContainer.scrollTop <= logsContainer.clientHeight + 10;
      if (state.buffered) {
        preElement.innerHTML = this.taskManager.parseAnsiColors(state.buffered);
      } else {
        preElement.innerHTML = '<span style="color: #888;">Waiting for logs...</span>';
      }
      if (atBottom) logsContainer.scrollTop = logsContainer.scrollHeight;
    };

    // Initial backlog via the API.
    try {
      const initial = await this.taskManager.readFullTaskLog(taskId);
      if (initial && initial.length) state.buffered = initial;
    } catch { /* fall through to placeholder */ }
    render();

    const onLog = (event) => {
      const detail = event.detail || {};
      if (detail.id !== taskId || typeof detail.chunk !== 'string') return;
      state.buffered += detail.chunk;
      render();
    };
    window.addEventListener('taskLog', onLog);

    // SSE catch-up: when the backend restarts mid-task (e.g., libreportal
    // recreates itself during a CrowdSec install), the SSE event source
    // drops. EventSource auto-reconnects, but task.log events emitted
    // during the gap are lost. taskBusReady fires after every reconnect —
    // pull the missed bytes via the existing /:id/log?position=N endpoint
    // and splice them in. Skips on the initial connect (no gap).
    let initialReadyFired = false;
    const onBusReady = async () => {
      if (!initialReadyFired) { initialReadyFired = true; return; }
      try {
        const res = await fetch(`/api/tasks/${encodeURIComponent(taskId)}/log?position=${state.buffered.length}`);
        if (!res.ok) return;
        const missed = await res.text();
        if (missed) {
          state.buffered += missed;
          render();
        }
      } catch { /* network blip, next ready will retry */ }
    };
    window.addEventListener('taskBusReady', onBusReady);

    this.activeLogStreams.set(taskId, {
      stream: { stop: () => {
        window.removeEventListener('taskLog', onLog);
        window.removeEventListener('taskBusReady', onBusReady);
        this.activeLogStreams.delete(taskId);
      } },
      render,
      isPaused: false
    });
  },
  // Stop log streaming for a task
  stopLogStreaming(taskId) {
    if (this.activeLogStreams && this.activeLogStreams.has(taskId)) {
      const streamData = this.activeLogStreams.get(taskId);
      streamData.stream.stop();
      this.activeLogStreams.delete(taskId);
    }
  },
  // Load task logs automatically
  async loadTaskLogs(taskId) {
    try {
      const logsContainer = document.getElementById(`logs-${taskId}`);
      if (!logsContainer) {
        console.warn(`⚠️ Logs container not found for task ${taskId}`);
        return;
      }

      const task = this.tasks.find(t => t.id === taskId);
      const inMemoryLog = (task && Array.isArray(task.log) && task.log.length > 0) ? task.log : null;

      const renderInMemory = () => {
        if (!inMemoryLog) return false;
        logsContainer.innerHTML = inMemoryLog
          .map(line => `<div class="log-entry">${this.taskManager.parseAnsiColors(line)}</div>`)
          .join('');
        return true;
      };

      logsContainer.innerHTML = '<div class="log-entry">🔄 Loading logs...</div>';
      const isScrolledToBottom = logsContainer.scrollHeight - logsContainer.scrollTop <= logsContainer.clientHeight + 10;

      const logResponse = await fetch(`/read-file?path=tasks/${taskId}.log`);
      if (logResponse.ok) {
        const logContent = await logResponse.text();
        if (logContent.trim()) {
          logsContainer.innerHTML = `<pre class="output-content terminal-style">${this.taskManager.parseAnsiColors(logContent)}</pre>`;
          if (isScrolledToBottom) logsContainer.scrollTop = logsContainer.scrollHeight;
          return;
        }
      }

      if (renderInMemory()) return;

      logsContainer.innerHTML = '<div class="log-entry">ℹ️ No logs available for this task.</div>';

    } catch (error) {
      console.error(`❌ Error loading logs for task ${taskId}:`, error);
      const logsContainer = document.getElementById(`logs-${taskId}`);
      if (logsContainer) {
        logsContainer.innerHTML = '<div class="log-entry">❌ Failed to load logs.</div>';
      }
    }
  },
  // Load task output on demand
  async loadTaskOutput(taskId) {
    try {
      // Read the task file to get output
      const task = await this.taskManager.getTask(taskId);
      if (!task) return;

      const outputElement = document.querySelector(`[data-task-id="${taskId}"] .task-output`);
      if (!outputElement) return;

      // Show loading state
      outputElement.innerHTML = `
        <div class="loading-output">Loading output...</div>
      `;

      // Check if task has output
      if (task.output && task.output.trim()) {
        outputElement.innerHTML = `
          <h4>📤 Output</h4>
          <pre class="output-content terminal-style">${this.taskManager.parseAnsiColors(task.output)}</pre>
        `;
      } else if (task.error && task.error.trim()) {
        outputElement.innerHTML = `
          <h4>❌ Error</h4>
          <pre class="error-content">${this.escapeHtml(task.error)}</pre>
        `;
      } else {
        // Try to read from log file
        const logResponse = await fetch(`/read-file?path=tasks/${taskId}.log`);
        if (logResponse.ok) {
          const logContent = await logResponse.text();
          if (logContent.trim()) {
            outputElement.innerHTML = `
              <pre class="output-content terminal-style">${this.taskManager.parseAnsiColors(logContent)}</pre>
            `;
          } else {
            outputElement.innerHTML = `
              <h4>ℹ️ Information</h4>
              <div class="info-content">No output available for this task.</div>
            `;
          }
        } else {
          outputElement.innerHTML = `
            <h4>ℹ️ Information</h4>
            <div class="info-content">No output available for this task.</div>
          `;
        }
      }
    } catch (error) {
      console.error(`❌ Error loading task output for ${taskId}:`, error);
      const outputElement = document.querySelector(`[data-task-id="${taskId}"] .task-output`);
      if (outputElement) {
        outputElement.innerHTML = `
          <h4>❌ Error</h4>
          <div class="error-content">Failed to load task output: ${error.message}</div>
        `;
      }
    }
  },
  // Start global live log updater - simple 2-second updates for all running tasks
  startGlobalLiveLogUpdater() {
    // Idempotent + keep the handle so unmount can clear it (it was discarded
    // before — a permanent 2 s poll that outlived the page).
    if (this.globalLiveLogInterval) return;
    // Update every 2 seconds
    this.globalLiveLogInterval = setInterval(async () => {
      
      // Find all running tasks
      const runningTasks = this.tasks.filter(task => task.status === 'running');
      
      if (runningTasks.length > 0) {
        
        // Update each running task's live logs
        for (const task of runningTasks) {
          await this.updateLiveLogsSimple(task.id);
        }
      } else {
      }
    }, 2000); // Every 2 seconds
  },
  // Simple live log update - no complex polling logic
  async updateLiveLogsSimple(taskId) {
    const liveLogsElement = document.getElementById(`live-logs-${taskId}`);
    if (!liveLogsElement) {
      return; // Silently skip if element not found
    }
    
    try {
      
      // Read the log file content
      const response = await fetch(`/read-file?path=tasks/${taskId}.log`);
      
      if (response.ok) {
        const logContent = await response.text();
        
        if (logContent.trim()) {
          // Split into lines and display
          const lines = logContent.split('\n').filter(line => line.trim());
          
          liveLogsElement.innerHTML = lines.map(line => 
            `<div class="log-entry">${this.parseAnsiColors(line)}</div>`
          ).join('');
          // Auto-scroll to bottom
          liveLogsElement.scrollTop = liveLogsElement.scrollHeight;
        } else {
          liveLogsElement.innerHTML = '<div class="log-entry">🔄 Waiting for logs...</div>';
        }
      } else {
        console.warn(`⚠️ Failed to read log file for task ${taskId}: ${response.status}`);
        liveLogsElement.innerHTML = '<div class="log-entry">⚠️ Unable to read logs</div>';
      }
    } catch (error) {
      // Silently handle errors
      console.warn(`⚠️ Error reading live logs for task ${taskId}:`, error);
      liveLogsElement.innerHTML = '<div class="log-entry">❌ Error loading logs</div>';
    }
  },
  // Update task structure for live logs
  updateTaskStructure(taskId, task) {
    const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
    if (!taskElement) return;
    
    const detailsElement = taskElement.querySelector('.task-details');
    if (!detailsElement) return;
    
    
    // Check if logs container already exists
    const existingLogs = detailsElement.querySelector('.task-logs .log-container');
    if (existingLogs) {
      return; // Already exists, no need to update
    }
    
    // Add simplified logs section for running tasks
    if (task.status === 'running') {
      const logsHtml = `
        <div class="task-logs">
          <div class="log-container terminal-style" id="logs-${taskId}" style="height: 200px; overflow-y: auto; position: relative;">
            <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>
      `;
      
      // Insert logs section at the bottom of details
      detailsElement.insertAdjacentHTML('beforeend', logsHtml);
      
      // Auto-load logs
      this.loadTaskLogs(taskId);
    }
  },
  // Update task display in real-time
  updateTaskDisplay(task) {
    const taskElement = document.querySelector(`[data-task-id="${task.id}"]`);
    if (!taskElement) {
      // The monitored task isn't always rendered (different tab, list filtered out,
      // task already removed). Silently skip — this is the normal case.
      return;
    }
    
    
    // Update status and content
    const statusElement = taskElement.querySelector('.task-status');
    const contentElement = taskElement.querySelector('.task-content');
    
    if (statusElement) {
      const statusClass = `status-${task.status || 'unknown'}`;
      statusElement.className = `task-status ${statusClass}`;
      statusElement.innerHTML = `${this.getStatusIcon(task.status)} ${task.status ? task.status.toUpperCase() : 'UNKNOWN'}`;
    } else {
      console.warn(`⚠️ Status element not found for task ${task.id}`);
    }

    // Mirror the status into the details panel's metadata block too — that
    // copy of the status was previously left stale until the page reloaded.
    const detailsStatus = taskElement.querySelector(`#details-${task.id} .task-meta .status-running, #details-${task.id} .task-meta .status-queued, #details-${task.id} .task-meta .status-pending, #details-${task.id} .task-meta .status-completed, #details-${task.id} .task-meta .status-failed, #details-${task.id} .task-meta .status-cancelled, #details-${task.id} .task-meta [class^="status-"]`);
    if (detailsStatus) {
      detailsStatus.className = `status-${task.status || 'unknown'}`;
      detailsStatus.innerHTML = `${this.getStatusIcon(task.status)} ${task.status ? task.status.toUpperCase() : 'UNKNOWN'}`;
    }

    if (contentElement) {
      contentElement.textContent = task.command;
    }
    
  },
  // Update highlighted task status and UI
  async updateHighlightedTaskStatus(taskId) {
    try {
      // Use lightweight summary for status updates
      const task = await this.taskManager.getTaskSummary(taskId);
      if (!task) return;
      
      
      // Update task display
      this.updateTaskDisplay(task);
      
      // If task completed or failed, always load output
      if ((task.status === 'completed' || task.status === 'failed')) {
        
        const details = document.getElementById(`details-${taskId}`);
        if (details && details.style.display === 'block') {
          // Load output regardless of current content
          this.loadTaskOutput(taskId);
        } else if (details) {
          // If details aren't open, mark for loading when opened
          details.setAttribute('data-load-output-on-open', 'true');
        }
      }
    } catch (error) {
      console.error(`❌ Error updating highlighted task status for ${taskId}:`, error);
    }
  }      ,
});
