/**
 * Background Task Queue — DB-backed task queue with scheduled execution.
 *
 * Tasks are linked to chat sessions (optional) so the worker can load
 * full conversation history for context. Supports scheduled execution
 * via scheduled_at field.
 */

import { getDb } from './db.mjs';

/**
 * Enqueue a new background task.
 * @param {{ subject: string, description?: string, sessionId?: string, scheduledAt?: string, skill?: string }} task
 * @returns {number} taskId
 */
export function enqueueTask({ subject, description = '', sessionId = null, scheduledAt = null, skill = null }) {
  if (!subject) throw new Error('subject is required');

  const result = getDb().prepare(`
    INSERT INTO background_tasks (session_id, subject, description, status, scheduled_at, skill)
    VALUES (?, ?, ?, 'pending', ?, ?)
  `).run(sessionId, subject, description, scheduledAt, skill);

  return Number(result.lastInsertRowid);
}

/**
 * Get the next eligible pending task.
 * Eligible: status='pending', scheduled_at is null or <= now, oldest first.
 * @returns {object|null}
 */
export function getNextTask() {
  return getDb().prepare(`
    SELECT * FROM background_tasks
    WHERE status = 'pending'
      AND (scheduled_at IS NULL OR scheduled_at <= datetime('now'))
    ORDER BY id ASC
    LIMIT 1
  `).get() || null;
}

/**
 * Get a task by ID.
 * @param {number} id
 * @returns {object|null}
 */
export function getTask(id) {
  return getDb().prepare('SELECT * FROM background_tasks WHERE id = ?').get(id) || null;
}

/**
 * List tasks with optional filtering.
 * @param {{ status?: string, limit?: number }} opts
 * @returns {Array}
 */
export function listTasks({ status, limit = 50 } = {}) {
  if (status) {
    return getDb().prepare(
      'SELECT * FROM background_tasks WHERE status = ? ORDER BY id DESC LIMIT ?'
    ).all(status, limit);
  }
  return getDb().prepare(
    'SELECT * FROM background_tasks ORDER BY id DESC LIMIT ?'
  ).all(limit);
}

/**
 * Update task status with optional fields.
 * @param {number} id
 * @param {string} status
 * @param {{ result?: string, error?: string, startedAt?: string, endedAt?: string }} fields
 */
export function setTaskStatus(id, status, { result, error, startedAt, endedAt } = {}) {
  const updates = ['status = ?'];
  const params = [status];

  if (result !== undefined) { updates.push('result = ?'); params.push(result); }
  if (error !== undefined) { updates.push('error = ?'); params.push(error); }
  if (startedAt !== undefined) { updates.push('started_at = ?'); params.push(startedAt); }
  if (endedAt !== undefined) { updates.push('ended_at = ?'); params.push(endedAt); }

  params.push(id);
  getDb().prepare(`UPDATE background_tasks SET ${updates.join(', ')} WHERE id = ?`).run(...params);
}

/**
 * Process the next eligible task using the active adapter.
 *
 * @param {{ broadcast?: function, addEvent?: function }} opts
 * @returns {{ taskId: number, status: string } | null}
 */
export async function processNextTask(opts = {}) {
  const task = getNextTask();
  if (!task) return null;

  const { broadcast, addEvent } = opts;
  const now = new Date().toISOString().replace('T', ' ').slice(0, 19);

  // Mark in_progress
  setTaskStatus(task.id, 'in_progress', { startedAt: now });
  if (broadcast) broadcast({ type: 'status', data: { state: 'bg_task_start', taskId: task.id, subject: task.subject } });

  try {
    let result;

    if (task.skill) {
      // Skill-based task: run the named skill with args from description
      const { runSkill } = await import('./skills.mjs');
      let args = {};
      try { args = task.description ? JSON.parse(task.description) : {}; } catch {}
      const skillResult = await runSkill(task.skill, args);
      result = typeof skillResult === 'string' ? skillResult : JSON.stringify(skillResult);
    } else {
      // LLM-based task: load session context and run prompt
      let sessionContext = '';
      if (task.session_id) {
        try {
          const { getSessionMessages } = await import('./session-store.mjs');
          const messages = getSessionMessages(task.session_id);
          if (messages.length > 0) {
            sessionContext = '\n\n## Conversation Context\n' +
              messages.map(m => `${m.role === 'user' ? 'Human' : 'Assistant'}: ${m.content}`).join('\n\n');
          }
        } catch {
          // Session store not available — continue without context
        }
      }

      const prompt = `You are an autonomous AI agent executing a background task.

## Task
Subject: ${task.subject}
${task.description ? `Description: ${task.description}` : ''}
${sessionContext}

## Instructions
Execute this task thoroughly. If the task involves creating files, write them to the workspace.
If the task involves research or analysis, provide comprehensive results.
Summarize what you accomplished at the end.`;

      const { askLLM, pickModel } = await import('./llm.mjs');
      result = await askLLM(prompt, { model: pickModel('build') });
    }

    const endNow = new Date().toISOString().replace('T', ' ').slice(0, 19);
    setTaskStatus(task.id, 'completed', { result: (result || '').slice(0, 10000), endedAt: endNow });
    if (broadcast) broadcast({ type: 'status', data: { state: 'bg_task_done', taskId: task.id, status: 'completed' } });
    if (addEvent) addEvent('system', 'agent', `Background task #${task.id} completed: ${task.subject}`);

    return { taskId: task.id, status: 'completed' };
  } catch (err) {
    const endNow = new Date().toISOString().replace('T', ' ').slice(0, 19);
    setTaskStatus(task.id, 'failed', { error: err.message, endedAt: endNow });
    if (broadcast) broadcast({ type: 'status', data: { state: 'bg_task_done', taskId: task.id, status: 'failed', error: err.message } });
    if (addEvent) addEvent('error', 'agent', `Background task #${task.id} failed: ${err.message}`);

    return { taskId: task.id, status: 'failed' };
  }
}
