import { getDb, newId, nowIso } from './db.js';
import { createHeartbeatTask } from './heartbeat-queue.js';
import { appendProjectChangelog } from './projects.js';
import {
  AGENT_RUN_EVENT_TYPES,
  LIVE_EVENT_TYPES,
  RUN_STATUSES,
  TASK_EVENT_TYPES,
  TASK_PRIORITIES,
  TASK_STATUSES,
  assertLegalTaskTransition,
  isActiveRunStatus,
  isTerminalTaskStatus,
  normalizeLiveEventType,
  normalizeRunStatus,
  normalizeTaskPriority,
  normalizeTaskStatus,
} from './delegation-contracts.js';

function parseJson(value, fallback = null) {
  if (!value) return fallback;
  try { return JSON.parse(value); } catch { return fallback; }
}

function stringifyJson(value) {
  return value == null ? null : JSON.stringify(value);
}

function broadcastLiveEvent(event) {
  const bus = globalThis.__madarxLiveEventBus;
  if (!bus?.subscribers) return {
    id: event.id || newId('evt'),
    ts: event.ts || Date.now(),
    ...event,
  };
  const enriched = {
    id: event.id || `evt-${bus.nextId++}-${Math.random().toString(36).slice(2, 8)}`,
    ts: event.ts || Date.now(),
    ...event,
  };
  for (const handler of bus.subscribers) {
    try {
      handler(enriched);
    } catch {
      // Ignore subscriber failures.
    }
  }
  return enriched;
}

function rowToAgent(row) {
  if (!row) return null;
  return {
    ...row,
    tools: parseJson(row.tools, []),
    files: parseJson(row.files, []),
    memory: parseJson(row.memory, []),
    permissions_json: parseJson(row.permissions_json, {}),
    metadata_json: parseJson(row.metadata_json, {}),
  };
}

function rowToTask(row) {
  if (!row) return null;
  return {
    ...row,
    parent_task_id: row.parent_task_id || null,
    tags: parseJson(row.tags_json, []),
    watchers: parseJson(row.watchers_json, []),
    due_at: row.due_at || null,
    estimate_minutes: row.estimate_minutes == null ? null : Number(row.estimate_minutes),
    recurrence: row.recurrence || null,
    acceptance_criteria: row.acceptance_criteria || null,
  };
}

function rowToChecklistItem(row) {
  if (!row) return null;
  return {
    id: row.id,
    task_id: row.task_id,
    label: row.label,
    done: !!row.done,
    position: Number(row.position) || 0,
    created_at: row.created_at,
    done_at: row.done_at || null,
  };
}

function normalizeTagsInput(value) {
  if (value == null) return null;
  if (Array.isArray(value)) {
    const clean = value
      .map((v) => String(v).trim())
      .filter(Boolean)
      .slice(0, 24);
    return clean.length ? clean : [];
  }
  if (typeof value === 'string') {
    const parts = value.split(',').map((p) => p.trim()).filter(Boolean);
    return parts.slice(0, 24);
  }
  return null;
}

function normalizeWatchersInput(value) {
  if (value == null) return null;
  const list = Array.isArray(value) ? value : String(value).split(',');
  const clean = list.map((v) => String(v).trim()).filter(Boolean).slice(0, 24);
  return Array.from(new Set(clean));
}

function normalizeDueAtInput(value) {
  if (!value) return null;
  const str = String(value).trim();
  if (!str) return null;
  if (/^\d{4}-\d{2}-\d{2}$/.test(str)) return `${str}T23:59:59.000Z`;
  const date = new Date(str);
  if (Number.isNaN(date.getTime())) return null;
  return date.toISOString();
}

function normalizeEstimateInput(value) {
  if (value == null || value === '') return null;
  const n = Number(value);
  if (!Number.isFinite(n) || n < 0) return null;
  return Math.round(n);
}

function normalizeRecurrenceInput(value) {
  if (!value) return null;
  const str = String(value).trim();
  if (!str) return null;
  if (str.length > 64) return str.slice(0, 64);
  return str;
}

function rowToTaskComment(row) {
  return row ? { ...row } : null;
}

function rowToTaskEvent(row) {
  return row ? { ...row, payload: parseJson(row.payload_json, null) } : null;
}

function rowToAgentRun(row) {
  return row ? {
    ...row,
    usage: parseJson(row.usage_json, null),
    result: parseJson(row.result_json, null),
  } : null;
}

function rowToAgentRunEvent(row) {
  return row ? {
    ...row,
    payload: parseJson(row.payload_json, null),
  } : null;
}

function rowToActivity(row) {
  return row ? {
    ...row,
    details: parseJson(row.details_json, null),
    cursor: row.seq,
  } : null;
}

function getAgentRow(id) {
  return rowToAgent(getDb().prepare('SELECT * FROM agents WHERE id = ?').get(id));
}

function requireAssignableAgent(agentId) {
  if (!agentId) return null;
  const agent = getAgentRow(agentId);
  if (!agent) throw new Error(`Agent not found: ${agentId}`);
  if (agent.status === 'terminated') throw new Error(`Terminated agents cannot receive assignments: ${agentId}`);
  return agent;
}

function computeTaskWakeReason(reason) {
  return ['task_assigned', 'task_reassigned', 'task_commented', 'task_blockers_resolved', 'child_tasks_completed', 'approval_resolved']
    .includes(reason)
    ? reason
    : 'task_assigned';
}

export function persistLiveEvent(event) {
  if (!event?.type || event.persist === false) return null;
  const mapped = mapLiveEventToActivity(event);
  if (!mapped) return null;
  return appendActivity(mapped);
}

function mapLiveEventToActivity(event) {
  const legacy = {
    agent_run_start: 'agent.run.status',
    agent_run_complete: 'agent.run.status',
    agent_tool_start: 'agent.run.event',
    agent_tool_result: 'agent.run.event',
    log: 'agent.run.log',
  };
  const action = normalizeLiveEventType(legacy[event.type] || event.type, null);
  if (!action || !LIVE_EVENT_TYPES.includes(action)) return null;
  return {
    actor_type: event.actor_type || (event.agent_id ? 'agent' : 'system'),
    actor_id: event.actor_id || event.agent_id || null,
    agent_id: event.agent_id || null,
    run_id: event.run_id || null,
    entity_type: event.entity_type || (event.task_id ? 'task' : event.run_id ? 'agent_run' : event.agent_id ? 'agent' : 'event'),
    entity_id: event.entity_id || event.task_id || event.run_id || event.id || null,
    action,
    details: {
      ...event,
      legacy_type: legacy[event.type] ? event.type : undefined,
    },
    created_at: event.created_at || nowIso(),
  };
}

export function appendActivity({
  actor_type = 'system',
  actor_id = null,
  agent_id = null,
  run_id = null,
  entity_type = null,
  entity_id = null,
  action,
  details = null,
  created_at = nowIso(),
} = {}) {
  if (!action) throw new Error('activity action is required');
  const db = getDb();
  const id = newId('activity');
  db.prepare(`
    INSERT INTO activity_log
      (id, actor_type, actor_id, agent_id, run_id, entity_type, entity_id, action, details_json, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(id, actor_type, actor_id, agent_id, run_id, entity_type, entity_id, action, stringifyJson(details), created_at);
  const row = db.prepare('SELECT seq, * FROM activity_log WHERE id = ?').get(id);
  return rowToActivity(row);
}

export function listActivity({ limit = 100, agentId, entityType, entityId, action, dateFrom, dateTo } = {}) {
  const db = getDb();
  const clauses = [];
  const values = [];
  if (agentId) { clauses.push('agent_id = ?'); values.push(agentId); }
  if (entityType) { clauses.push('entity_type = ?'); values.push(entityType); }
  if (entityId) { clauses.push('entity_id = ?'); values.push(entityId); }
  if (action) { clauses.push('action = ?'); values.push(action); }
  if (dateFrom) { clauses.push('created_at >= ?'); values.push(dateFrom); }
  if (dateTo) { clauses.push('created_at <= ?'); values.push(dateTo); }
  const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
  return db.prepare(`SELECT seq, * FROM activity_log ${where} ORDER BY seq DESC LIMIT ?`).all(...values, limit).map(rowToActivity);
}

export function listActivitySince(cursor = 0, limit = 200) {
  return getDb().prepare('SELECT seq, * FROM activity_log WHERE seq > ? ORDER BY seq ASC LIMIT ?')
    .all(Number(cursor) || 0, limit).map(rowToActivity);
}

export function getMaxActivitySeq() {
  const row = getDb().prepare('SELECT COALESCE(MAX(seq), 0) AS max_seq FROM activity_log').get();
  return Number(row?.max_seq || 0);
}

function getDirectReports(managerId) {
  return getDb().prepare('SELECT * FROM agents WHERE reports_to = ? ORDER BY name ASC').all(managerId).map(rowToAgent);
}

function assertAcyclicReports(agentId, reportsTo) {
  if (!reportsTo) return;
  if (agentId === reportsTo) throw new Error('Agent cannot report to itself');
  const seen = new Set([agentId]);
  let cursor = reportsTo;
  while (cursor) {
    if (seen.has(cursor)) throw new Error('Reporting graph cannot contain a cycle');
    seen.add(cursor);
    const row = getDb().prepare('SELECT reports_to, status FROM agents WHERE id = ?').get(cursor);
    if (!row) throw new Error(`Manager not found: ${cursor}`);
    if (row.status === 'terminated') throw new Error('Reporting links cannot point to terminated agents');
    cursor = row.reports_to;
  }
}

export function updateAgentOrg(agentId, patch = {}) {
  const db = getDb();
  const agent = getAgentRow(agentId);
  if (!agent) throw new Error(`Agent not found: ${agentId}`);
  const nextReportsTo = patch.reports_to === undefined ? agent.reports_to : (patch.reports_to || null);
  assertAcyclicReports(agentId, nextReportsTo);
  const allowed = ['reports_to', 'role', 'title', 'status_reason', 'autonomy_mode', 'budget_monthly_cents', 'spent_monthly_cents', 'permissions_json', 'metadata_json'];
  const entries = Object.entries(patch).filter(([key]) => allowed.includes(key));
  if (!entries.length) return agent;
  const sets = entries.map(([key]) => `${key} = ?`).join(', ');
  const values = entries.map(([key, value]) => (key.endsWith('_json') ? stringifyJson(value || {}) : value ?? null));
  db.prepare(`UPDATE agents SET ${sets} WHERE id = ?`).run(...values, agentId);
  return getAgentRow(agentId);
}

export function getOrgChart() {
  const agents = getDb().prepare('SELECT * FROM agents ORDER BY name ASC').all().map(rowToAgent);
  const childrenByParent = new Map();
  for (const agent of agents) {
    const key = agent.reports_to || '__root__';
    if (!childrenByParent.has(key)) childrenByParent.set(key, []);
    childrenByParent.get(key).push(agent);
  }
  const build = (agent) => ({
    ...agent,
    reports: (childrenByParent.get(agent.id) || []).map(build),
  });
  return (childrenByParent.get('__root__') || []).map(build);
}

export function getChain(agentId) {
  const chain = [];
  let cursor = getAgentRow(agentId);
  while (cursor) {
    chain.push(cursor);
    cursor = cursor.reports_to ? getAgentRow(cursor.reports_to) : null;
  }
  return chain;
}

export function getReports(managerId) {
  return getDirectReports(managerId);
}

function appendTaskEvent(taskId, {
  event_type,
  actor_type = 'system',
  actor_id = null,
  agent_id = null,
  run_id = null,
  payload = null,
  created_at = nowIso(),
} = {}) {
  if (!TASK_EVENT_TYPES.includes(event_type)) throw new Error(`Invalid task event type: ${event_type}`);
  const db = getDb();
  const id = newId('taskevent');
  db.prepare(`
    INSERT INTO task_events
      (id, task_id, event_type, actor_type, actor_id, agent_id, run_id, payload_json, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(id, taskId, event_type, actor_type, actor_id, agent_id, run_id, stringifyJson(payload), created_at);
  return rowToTaskEvent(db.prepare('SELECT * FROM task_events WHERE id = ?').get(id));
}

function queueTaskWake({ task, reason, commentId = null, actorId = null, triggerSource = 'task' } = {}) {
  if (!task?.assignee_agent_id || isTerminalTaskStatus(task.status)) return null;
  return createHeartbeatTask({
    type: 'agent-task',
    agent_id: task.assignee_agent_id,
    priority: reason === 'task_assigned' || reason === 'task_reassigned' ? 10 : 5,
    payload: {
      type: 'agent-task',
      task_id: task.id,
      agent_id: task.assignee_agent_id,
      reason: computeTaskWakeReason(reason),
      comment_id: commentId,
      parent_task_id: task.parent_task_id || null,
      trigger_source: triggerSource,
      actor_id: actorId,
    },
  });
}

function recordTaskMutation(task, { action, liveType, actor_type = 'system', actor_id = null, agent_id = null, run_id = null, details = null, event_type = 'task.updated', wake_reason = null } = {}) {
  const taskEvent = appendTaskEvent(task.id, { event_type, actor_type, actor_id, agent_id, run_id, payload: details });
  const activity = appendActivity({
    actor_type,
    actor_id,
    agent_id: agent_id || task.assignee_agent_id || null,
    run_id,
    entity_type: 'task',
    entity_id: task.id,
    action,
    details: { task, ...details },
  });
  broadcastLiveEvent({
    type: liveType || action,
    task_id: task.id,
    agent_id: task.assignee_agent_id || null,
    actor_type,
    actor_id,
    run_id,
    entity_type: 'task',
    entity_id: task.id,
    details,
    persist: false,
  });
  if (task.project_id) {
    try {
      const verb = (() => {
        if (event_type === 'task.created' || details?.event === 'created') return 'Created';
        if (event_type === 'task.assigned') return 'Assigned';
        if (event_type === 'task.status_changed') return `${task.status === 'done' ? 'Completed' : 'Updated'}`;
        if (task.status === 'done') return 'Completed';
        if (task.status === 'cancelled') return 'Cancelled';
        return 'Updated';
      })();
      const isHeadline = ['task.created', 'task.status_changed', 'task.assigned'].includes(event_type)
        || task.status === 'done'
        || task.status === 'cancelled';
      appendProjectChangelog(task.project_id, {
        action: event_type || action || 'task.updated',
        actor_type,
        actor_id,
        agent_id: agent_id || task.assignee_agent_id || null,
        run_id,
        title: `${verb} task: ${task.title}`,
        summary: `${verb} task "${task.title}" (status: ${task.status})`,
        status: task.status,
        source: 'task',
        update_kind: isHeadline ? 'progress' : null,
        details: { task_id: task.id, status: task.status, priority: task.priority, folder_id: null },
      });
    } catch {}
  }
  if (wake_reason) queueTaskWake({ task, reason: wake_reason, actorId: actor_id, triggerSource: action });
  return { taskEvent, activity };
}

export function listTasks({ assignee_agent_id, status, priority, parent_task_id, project_id, limit = 100 } = {}) {
  const clauses = [];
  const values = [];
  if (assignee_agent_id) { clauses.push('assignee_agent_id = ?'); values.push(assignee_agent_id); }
  if (status) { clauses.push('status = ?'); values.push(normalizeTaskStatus(status)); }
  if (priority) { clauses.push('priority = ?'); values.push(normalizeTaskPriority(priority)); }
  if (parent_task_id === null) clauses.push('parent_task_id IS NULL');
  else if (parent_task_id) { clauses.push('parent_task_id = ?'); values.push(parent_task_id); }
  if (project_id === null) clauses.push('project_id IS NULL');
  else if (project_id) { clauses.push('project_id = ?'); values.push(project_id); }
  const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
  return getDb().prepare(`SELECT * FROM tasks ${where} ORDER BY updated_at DESC LIMIT ?`).all(...values, limit).map(rowToTask);
}

export function getTask(id) {
  return rowToTask(getDb().prepare('SELECT * FROM tasks WHERE id = ?').get(id));
}

export function getTaskChildren(id) {
  return getDb().prepare('SELECT * FROM tasks WHERE parent_task_id = ? ORDER BY updated_at DESC').all(id).map(rowToTask);
}

export function listTaskComments(taskId) {
  return getDb().prepare('SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC').all(taskId).map(rowToTaskComment);
}

export function listTaskEvents(taskId) {
  return getDb().prepare('SELECT * FROM task_events WHERE task_id = ? ORDER BY created_at ASC').all(taskId).map(rowToTaskEvent);
}

export function createTask(payload = {}) {
  const db = getDb();
  const now = nowIso();
  const id = payload.id || newId('task');
  const assignee = payload.assignee_agent_id ? requireAssignableAgent(payload.assignee_agent_id) : null;
  const status = normalizeTaskStatus(payload.status, assignee ? 'todo' : 'backlog');
  if (status === 'in_progress' && !assignee) throw new Error('in_progress requires assignee_agent_id');
  const priority = normalizeTaskPriority(payload.priority);
  const tags = normalizeTagsInput(payload.tags);
  const watchers = normalizeWatchersInput(payload.watchers);
  const dueAt = normalizeDueAtInput(payload.due_at);
  const estimate = normalizeEstimateInput(payload.estimate_minutes);
  const recurrence = normalizeRecurrenceInput(payload.recurrence);
  db.prepare(`
    INSERT INTO tasks
      (id, workspace_id, title, description, status, priority, assignee_agent_id, created_by_agent_id, created_by_user_id,
       parent_task_id, project_id, goal_id, checkout_run_id, execution_run_id, blocked_reason,
       due_at, tags_json, estimate_minutes, watchers_json, recurrence, acceptance_criteria, recurred_from_task_id,
       created_at, updated_at, started_at, completed_at, cancelled_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?,  ?, ?, ?, ?, ?, ?, ?,  ?, ?, ?, ?, ?)
  `).run(
    id,
    payload.workspace_id || 'local',
    String(payload.title || 'Untitled task'),
    payload.description || null,
    status,
    priority,
    assignee?.id || null,
    payload.created_by_agent_id || null,
    payload.created_by_user_id || null,
    payload.parent_task_id || null,
    payload.project_id || null,
    payload.goal_id || null,
    payload.blocked_reason || null,
    dueAt,
    stringifyJson(tags),
    estimate,
    stringifyJson(watchers),
    recurrence,
    payload.acceptance_criteria || null,
    payload.recurred_from_task_id || null,
    now,
    now,
    status === 'in_progress' ? now : null,
    status === 'done' ? now : null,
    status === 'cancelled' ? now : null,
  );
  if (Array.isArray(payload.checklist) && payload.checklist.length) {
    const checklistStmt = db.prepare(`
      INSERT INTO task_checklist_items (id, task_id, label, done, position, created_at, done_at)
      VALUES (?, ?, ?, 0, ?, ?, NULL)
    `);
    payload.checklist
      .map((l) => String(l).trim())
      .filter(Boolean)
      .slice(0, 30)
      .forEach((label, idx) => {
        checklistStmt.run(newId('ckl'), id, label, idx, now);
      });
  }
  const task = getTask(id);
  recordTaskMutation(task, {
    action: 'task.updated',
    liveType: 'task.updated',
    actor_type: payload.created_by_agent_id ? 'agent' : payload.created_by_user_id ? 'user' : 'system',
    actor_id: payload.created_by_agent_id || payload.created_by_user_id || null,
    agent_id: assignee?.id || null,
    details: { event: 'created' },
    event_type: 'task.created',
    wake_reason: assignee ? 'task_assigned' : null,
  });
  if (assignee) {
    recordTaskMutation(task, {
      action: 'task.assigned',
      liveType: 'task.assigned',
      actor_type: payload.created_by_agent_id ? 'agent' : payload.created_by_user_id ? 'user' : 'system',
      actor_id: payload.created_by_agent_id || payload.created_by_user_id || null,
      agent_id: assignee.id,
      details: { assignee_agent_id: assignee.id },
      event_type: 'task.assigned',
    });
  }
  if (payload.parent_task_id) {
    appendTaskEvent(payload.parent_task_id, {
      event_type: 'task.child_created',
      actor_type: payload.created_by_agent_id ? 'agent' : payload.created_by_user_id ? 'user' : 'system',
      actor_id: payload.created_by_agent_id || payload.created_by_user_id || null,
      agent_id: assignee?.id || null,
      payload: { child_task_id: task.id },
    });
  }
  return task;
}

export function updateTask(id, patch = {}) {
  const db = getDb();
  const current = getTask(id);
  if (!current) throw new Error(`Task not found: ${id}`);
  const nextStatus = patch.status === undefined ? current.status : normalizeTaskStatus(patch.status, current.status);
  const nextPriority = patch.priority === undefined ? current.priority : normalizeTaskPriority(patch.priority, current.priority);
  const assignee = patch.assignee_agent_id === undefined
    ? (current.assignee_agent_id ? requireAssignableAgent(current.assignee_agent_id) : null)
    : (patch.assignee_agent_id ? requireAssignableAgent(patch.assignee_agent_id) : null);
  assertLegalTaskTransition(current.status, nextStatus);
  if (nextStatus === 'in_progress' && !assignee?.id) throw new Error('in_progress requires assignee_agent_id');

  const clearingExecution = current.assignee_agent_id !== (assignee?.id || null) || (current.status === 'in_progress' && nextStatus !== 'in_progress');
  const tags = patch.tags === undefined ? current.tags : (normalizeTagsInput(patch.tags) || []);
  const watchers = patch.watchers === undefined ? current.watchers : (normalizeWatchersInput(patch.watchers) || []);
  const dueAt = patch.due_at === undefined ? current.due_at : normalizeDueAtInput(patch.due_at);
  const estimate = patch.estimate_minutes === undefined ? current.estimate_minutes : normalizeEstimateInput(patch.estimate_minutes);
  const recurrence = patch.recurrence === undefined ? current.recurrence : normalizeRecurrenceInput(patch.recurrence);
  const acceptance = patch.acceptance_criteria === undefined ? current.acceptance_criteria : (patch.acceptance_criteria || null);
  const updates = {
    title: patch.title ?? current.title,
    description: patch.description ?? current.description,
    status: nextStatus,
    priority: nextPriority,
    assignee_agent_id: assignee?.id || null,
    blocked_reason: nextStatus === 'blocked' ? (patch.blocked_reason ?? current.blocked_reason ?? 'Blocked') : (patch.blocked_reason ?? null),
    due_at: dueAt,
    tags_json: stringifyJson(tags),
    estimate_minutes: estimate,
    watchers_json: stringifyJson(watchers),
    recurrence,
    acceptance_criteria: acceptance,
    checkout_run_id: clearingExecution ? null : current.checkout_run_id,
    execution_run_id: clearingExecution ? null : current.execution_run_id,
    updated_at: nowIso(),
    started_at: current.started_at || (nextStatus === 'in_progress' ? nowIso() : null),
    completed_at: nextStatus === 'done' ? (current.completed_at || nowIso()) : null,
    cancelled_at: nextStatus === 'cancelled' ? (current.cancelled_at || nowIso()) : null,
  };

  db.prepare(`
    UPDATE tasks SET
      title = ?, description = ?, status = ?, priority = ?, assignee_agent_id = ?, blocked_reason = ?,
      due_at = ?, tags_json = ?, estimate_minutes = ?, watchers_json = ?, recurrence = ?, acceptance_criteria = ?,
      checkout_run_id = ?, execution_run_id = ?, updated_at = ?, started_at = ?, completed_at = ?, cancelled_at = ?
    WHERE id = ?
  `).run(
    updates.title,
    updates.description,
    updates.status,
    updates.priority,
    updates.assignee_agent_id,
    updates.blocked_reason,
    updates.due_at,
    updates.tags_json,
    updates.estimate_minutes,
    updates.watchers_json,
    updates.recurrence,
    updates.acceptance_criteria,
    updates.checkout_run_id,
    updates.execution_run_id,
    updates.updated_at,
    updates.started_at,
    updates.completed_at,
    updates.cancelled_at,
    id
  );
  const task = getTask(id);
  const actor_type = patch.actor_type || 'system';
  const actor_id = patch.actor_id || null;

  if (current.assignee_agent_id !== task.assignee_agent_id) {
    recordTaskMutation(task, {
      action: current.assignee_agent_id ? 'task.reassigned' : 'task.assigned',
      liveType: current.assignee_agent_id ? 'task.reassigned' : 'task.assigned',
      actor_type,
      actor_id,
      agent_id: task.assignee_agent_id,
      details: { from: current.assignee_agent_id, to: task.assignee_agent_id, execution_lock_cleared: clearingExecution },
      event_type: current.assignee_agent_id ? 'task.reassigned' : 'task.assigned',
      wake_reason: current.assignee_agent_id ? 'task_reassigned' : 'task_assigned',
    });
  }
  if (current.status !== task.status) {
    recordTaskMutation(task, {
      action: 'task.updated',
      liveType: 'task.updated',
      actor_type,
      actor_id,
      agent_id: task.assignee_agent_id,
      details: { from: current.status, to: task.status, execution_lock_cleared: clearingExecution },
      event_type: task.status === 'blocked'
        ? 'task.blocked'
        : task.status === 'done'
          ? 'task.completed'
          : task.status === 'cancelled'
            ? 'task.cancelled'
            : current.status === 'blocked'
              ? 'task.unblocked'
              : 'task.status_changed',
      wake_reason: current.status === 'blocked' && task.status !== 'blocked' ? 'task_blockers_resolved' : null,
    });
  }
  if (patch.title !== undefined || patch.description !== undefined || patch.priority !== undefined
      || patch.due_at !== undefined || patch.tags !== undefined || patch.estimate_minutes !== undefined
      || patch.watchers !== undefined || patch.recurrence !== undefined || patch.acceptance_criteria !== undefined) {
    const changed = ['title','description','priority','due_at','tags','estimate_minutes','watchers','recurrence','acceptance_criteria']
      .filter((k) => patch[k] !== undefined);
    recordTaskMutation(task, {
      action: 'task.updated',
      liveType: 'task.updated',
      actor_type,
      actor_id,
      agent_id: task.assignee_agent_id,
      details: { fields: changed },
      event_type: 'task.updated',
    });
  }
  if (current.status !== 'done' && task.status === 'done' && task.recurrence && !task.recurred_from_task_id) {
    try {
      materializeRecurrence(task, { actor_type, actor_id });
    } catch (err) {
      console.warn(`[delegation] recurrence rollover failed for task ${task.id}:`, err?.message || err);
    }
  }
  return task;
}

function materializeRecurrence(parentTask, { actor_type = 'system', actor_id = null } = {}) {
  const nextDueAt = computeNextRecurrence(parentTask.recurrence, parentTask.due_at || nowIso());
  if (!nextDueAt) return null;
  const checklistTemplate = listTaskChecklist(parentTask.id).map((c) => c.label);
  return createTask({
    title: parentTask.title,
    description: parentTask.description,
    project_id: parentTask.project_id,
    assignee_agent_id: parentTask.assignee_agent_id,
    priority: parentTask.priority,
    parent_task_id: parentTask.parent_task_id,
    goal_id: parentTask.goal_id,
    due_at: nextDueAt,
    tags: parentTask.tags,
    watchers: parentTask.watchers,
    estimate_minutes: parentTask.estimate_minutes,
    recurrence: parentTask.recurrence,
    acceptance_criteria: parentTask.acceptance_criteria,
    recurred_from_task_id: parentTask.id,
    checklist: checklistTemplate,
    created_by_agent_id: actor_type === 'agent' ? actor_id : null,
    created_by_user_id: actor_type === 'user' ? actor_id : null,
    status: parentTask.assignee_agent_id ? 'todo' : 'backlog',
  });
}

function computeNextRecurrence(spec, fromIso) {
  if (!spec) return null;
  const base = new Date(fromIso || Date.now());
  if (Number.isNaN(base.getTime())) return null;
  const trimmed = String(spec).trim().toLowerCase();
  const everyMatch = trimmed.match(/^every\s+(\d+)\s*(m|h|d|w)$/);
  if (everyMatch) {
    const n = Number(everyMatch[1]);
    const unit = everyMatch[2];
    const ms = unit === 'm' ? n * 60_000
      : unit === 'h' ? n * 3_600_000
      : unit === 'd' ? n * 86_400_000
      : n * 7 * 86_400_000;
    return new Date(base.getTime() + ms).toISOString();
  }
  if (trimmed === 'daily' || /^daily\b/.test(trimmed)) {
    return new Date(base.getTime() + 86_400_000).toISOString();
  }
  if (trimmed === 'weekly' || /^weekly\b/.test(trimmed)) {
    return new Date(base.getTime() + 7 * 86_400_000).toISOString();
  }
  if (trimmed === 'monthly' || /^monthly\b/.test(trimmed)) {
    const d = new Date(base);
    d.setUTCMonth(d.getUTCMonth() + 1);
    return d.toISOString();
  }
  return null;
}

export function listTaskChecklist(taskId) {
  return getDb()
    .prepare('SELECT * FROM task_checklist_items WHERE task_id = ? ORDER BY position ASC, created_at ASC')
    .all(taskId)
    .map(rowToChecklistItem);
}

export function addChecklistItem(taskId, { label, position } = {}) {
  const task = getTask(taskId);
  if (!task) throw new Error(`Task not found: ${taskId}`);
  const clean = String(label || '').trim();
  if (!clean) throw new Error('label is required');
  const db = getDb();
  const existing = listTaskChecklist(taskId);
  const pos = position == null ? existing.length : Math.max(0, Math.min(existing.length, Number(position)));
  const id = newId('ckl');
  db.prepare(`
    INSERT INTO task_checklist_items (id, task_id, label, done, position, created_at, done_at)
    VALUES (?, ?, ?, 0, ?, ?, NULL)
  `).run(id, taskId, clean.slice(0, 240), pos, nowIso());
  return rowToChecklistItem(db.prepare('SELECT * FROM task_checklist_items WHERE id = ?').get(id));
}

export function updateChecklistItem(itemId, patch = {}) {
  const db = getDb();
  const current = db.prepare('SELECT * FROM task_checklist_items WHERE id = ?').get(itemId);
  if (!current) throw new Error(`Checklist item not found: ${itemId}`);
  const label = patch.label === undefined ? current.label : String(patch.label || '').trim().slice(0, 240);
  const done = patch.done === undefined ? !!current.done : !!patch.done;
  const doneAt = done ? (current.done_at || nowIso()) : null;
  db.prepare('UPDATE task_checklist_items SET label = ?, done = ?, done_at = ? WHERE id = ?')
    .run(label, done ? 1 : 0, doneAt, itemId);
  return rowToChecklistItem(db.prepare('SELECT * FROM task_checklist_items WHERE id = ?').get(itemId));
}

export function deleteChecklistItem(itemId) {
  getDb().prepare('DELETE FROM task_checklist_items WHERE id = ?').run(itemId);
  return { ok: true };
}

export function listTaskAttachments(taskId) {
  return getDb()
    .prepare('SELECT id, run_id, agent_id, path, kind, title, created_at FROM artifacts WHERE task_id = ? ORDER BY created_at DESC')
    .all(taskId);
}

export function attachArtifactToTask(artifactId, taskId) {
  getDb().prepare('UPDATE artifacts SET task_id = ? WHERE id = ?').run(taskId, artifactId);
  return { ok: true };
}

export function addTaskComment(taskId, { body, author_agent_id = null, author_user_id = null, created_by_run_id = null } = {}) {
  const task = getTask(taskId);
  if (!task) throw new Error(`Task not found: ${taskId}`);
  const db = getDb();
  const id = newId('comment');
  const created_at = nowIso();
  db.prepare(`
    INSERT INTO task_comments (id, task_id, author_agent_id, author_user_id, created_by_run_id, body, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(id, taskId, author_agent_id, author_user_id, created_by_run_id, String(body || '').trim(), created_at);
  appendTaskEvent(taskId, {
    event_type: 'task.comment_added',
    actor_type: author_agent_id ? 'agent' : author_user_id ? 'user' : 'system',
    actor_id: author_agent_id || author_user_id || null,
    agent_id: task.assignee_agent_id || null,
    run_id: created_by_run_id,
    payload: { comment_id: id },
  });
  appendActivity({
    actor_type: author_agent_id ? 'agent' : author_user_id ? 'user' : 'system',
    actor_id: author_agent_id || author_user_id || null,
    agent_id: task.assignee_agent_id || null,
    run_id: created_by_run_id,
    entity_type: 'task',
    entity_id: taskId,
    action: 'task.comment.added',
    details: { comment_id: id, body: String(body || '').slice(0, 400) },
  });
  broadcastLiveEvent({
    type: 'task.comment.added',
    task_id: taskId,
    agent_id: task.assignee_agent_id || null,
    actor_type: author_agent_id ? 'agent' : author_user_id ? 'user' : 'system',
    actor_id: author_agent_id || author_user_id || null,
    run_id: created_by_run_id,
    entity_type: 'task',
    entity_id: taskId,
    details: { comment_id: id },
    persist: false,
  });
  if (task.assignee_agent_id && task.assignee_agent_id !== author_agent_id) {
    queueTaskWake({ task, reason: 'task_commented', commentId: id, actorId: author_agent_id || author_user_id || null, triggerSource: 'task.comment.added' });
  }
  return rowToTaskComment(db.prepare('SELECT * FROM task_comments WHERE id = ?').get(id));
}

export function checkoutTask(taskId, { run_id = null } = {}) {
  const db = getDb();
  const lockId = run_id || newId('checkout');
  const now = nowIso();
  const result = db.prepare(`
    UPDATE tasks
    SET checkout_run_id = ?, execution_run_id = COALESCE(execution_run_id, ?), updated_at = ?, started_at = COALESCE(started_at, ?), status = CASE WHEN status = 'todo' THEN 'in_progress' ELSE status END
    WHERE id = ? AND assignee_agent_id IS NOT NULL AND (checkout_run_id IS NULL OR checkout_run_id = ?)
  `).run(lockId, run_id || null, now, now, taskId, lockId);
  if (!result.changes) throw new Error('Task checkout failed');
  return getTask(taskId);
}

export function releaseTask(taskId, { clearExecution = true } = {}) {
  const db = getDb();
  db.prepare(`
    UPDATE tasks
    SET checkout_run_id = NULL, execution_run_id = CASE WHEN ? THEN NULL ELSE execution_run_id END, updated_at = ?
    WHERE id = ?
  `).run(clearExecution ? 1 : 0, nowIso(), taskId);
  return getTask(taskId);
}

function nextRunEventSeq(runId) {
  const row = getDb().prepare('SELECT COALESCE(MAX(seq), 0) AS max_seq FROM agent_run_events WHERE run_id = ?').get(runId);
  return Number(row?.max_seq || 0) + 1;
}

export function createAgentRun({
  agent_id,
  task_id = null,
  trigger_source = 'manual',
  trigger_detail = null,
  status = 'queued',
  session_id_before = null,
} = {}) {
  if (!agent_id) throw new Error('agent_id is required');
  const db = getDb();
  const now = nowIso();
  const id = newId('arun');
  db.prepare(`
    INSERT INTO agent_runs
      (id, agent_id, task_id, trigger_source, trigger_detail, status, started_at, finished_at, error,
       session_id_before, session_id_after, usage_json, result_json, stdout_excerpt, stderr_excerpt, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?, NULL, NULL, NULL, ?, NULL, NULL, NULL, NULL, NULL, ?, ?)
  `).run(id, agent_id, task_id, trigger_source, trigger_detail, normalizeRunStatus(status), session_id_before, now, now);
  appendAgentRunEvent(id, { agent_id, task_id, event_type: 'lifecycle', level: 'info', message: 'Run queued', payload: { status: 'queued' } });
  appendActivity({
    actor_type: 'agent',
    actor_id: agent_id,
    agent_id,
    run_id: id,
    entity_type: task_id ? 'task' : 'agent_run',
    entity_id: task_id || id,
    action: 'agent.run.queued',
    details: { trigger_source, trigger_detail, task_id },
  });
  broadcastLiveEvent({
    type: 'agent.run.queued',
    agent_id,
    run_id: id,
    task_id,
    entity_type: task_id ? 'task' : 'agent_run',
    entity_id: task_id || id,
    details: { trigger_source, trigger_detail },
    persist: false,
  });
  return getAgentRun(id);
}

export function getAgentRun(id) {
  return rowToAgentRun(getDb().prepare('SELECT * FROM agent_runs WHERE id = ?').get(id));
}

export function listAgentRuns({ task_id, agent_id, status, limit = 100 } = {}) {
  const clauses = [];
  const values = [];
  if (task_id) { clauses.push('task_id = ?'); values.push(task_id); }
  if (agent_id) { clauses.push('agent_id = ?'); values.push(agent_id); }
  if (status) { clauses.push('status = ?'); values.push(status); }
  const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
  return getDb().prepare(`SELECT * FROM agent_runs ${where} ORDER BY created_at DESC LIMIT ?`).all(...values, limit).map(rowToAgentRun);
}

export function listAgentRunEvents(runId) {
  return getDb().prepare('SELECT * FROM agent_run_events WHERE run_id = ? ORDER BY seq ASC').all(runId).map(rowToAgentRunEvent);
}

export function appendAgentRunEvent(runId, {
  agent_id = null,
  task_id = null,
  event_type = 'log',
  stream = 'system',
  level = 'info',
  message = '',
  payload = null,
  created_at = nowIso(),
} = {}) {
  if (!AGENT_RUN_EVENT_TYPES.includes(event_type)) throw new Error(`Invalid run event type: ${event_type}`);
  const db = getDb();
  const id = newId('arunevt');
  const seq = nextRunEventSeq(runId);
  db.prepare(`
    INSERT INTO agent_run_events
      (id, run_id, agent_id, task_id, seq, event_type, stream, level, message, payload_json, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(id, runId, agent_id, task_id, seq, event_type, stream, level, message || null, stringifyJson(payload), created_at);
  const event = rowToAgentRunEvent(db.prepare('SELECT * FROM agent_run_events WHERE id = ?').get(id));
  broadcastLiveEvent({
    type: event_type === 'log' ? 'agent.run.log' : 'agent.run.event',
    agent_id,
    run_id: runId,
    task_id,
    entity_type: task_id ? 'task' : 'agent_run',
    entity_id: task_id || runId,
    details: { event_type, level, message, payload, seq },
    persist: false,
  });
  return event;
}

export function updateAgentRun(id, patch = {}) {
  const current = getAgentRun(id);
  if (!current) throw new Error(`Run not found: ${id}`);
  const db = getDb();
  const nextStatus = patch.status ? normalizeRunStatus(patch.status, current.status) : current.status;
  const now = nowIso();
  db.prepare(`
    UPDATE agent_runs
    SET status = ?, started_at = COALESCE(?, started_at), finished_at = ?, error = ?, session_id_after = COALESCE(?, session_id_after),
        usage_json = COALESCE(?, usage_json), result_json = COALESCE(?, result_json), stdout_excerpt = COALESCE(?, stdout_excerpt),
        stderr_excerpt = COALESCE(?, stderr_excerpt), updated_at = ?
    WHERE id = ?
  `).run(
    nextStatus,
    patch.started_at || null,
    patch.finished_at || null,
    patch.error ?? null,
    patch.session_id_after || null,
    patch.usage ? stringifyJson(patch.usage) : null,
    patch.result ? stringifyJson(patch.result) : null,
    patch.stdout_excerpt || null,
    patch.stderr_excerpt || null,
    now,
    id
  );
  appendActivity({
    actor_type: 'agent',
    actor_id: current.agent_id,
    agent_id: current.agent_id,
    run_id: id,
    entity_type: current.task_id ? 'task' : 'agent_run',
    entity_id: current.task_id || id,
    action: 'agent.run.status',
    details: { status: nextStatus, error: patch.error || null },
  });
  broadcastLiveEvent({
    type: 'agent.run.status',
    agent_id: current.agent_id,
    run_id: id,
    task_id: current.task_id,
    entity_type: current.task_id ? 'task' : 'agent_run',
    entity_id: current.task_id || id,
    details: { status: nextStatus, error: patch.error || null },
    persist: false,
  });
  return getAgentRun(id);
}

export function markAgentRunRunning(id) {
  const current = getAgentRun(id);
  const now = nowIso();
  const run = updateAgentRun(id, { status: 'running', started_at: current?.started_at || now });
  appendAgentRunEvent(id, { agent_id: run.agent_id, task_id: run.task_id, event_type: 'lifecycle', message: 'Run started', payload: { status: 'running' } });
  if (run.task_id) {
    checkoutTask(run.task_id, { run_id: id });
  }
  return run;
}

export function markAgentRunWaitingApproval(id, { message = 'Approval required', payload = null } = {}) {
  const run = updateAgentRun(id, { status: 'waiting_approval' });
  appendAgentRunEvent(id, { agent_id: run.agent_id, task_id: run.task_id, event_type: 'approval_required', level: 'warn', message, payload });
  return run;
}

export function markAgentRunFinished(id, { status = 'succeeded', error = null, usage = null, result = null, stdout_excerpt = null, stderr_excerpt = null, session_id_after = null } = {}) {
  const terminal = normalizeRunStatus(status, 'succeeded');
  const finished_at = nowIso();
  const run = updateAgentRun(id, { status: terminal, finished_at, error, usage, result, stdout_excerpt, stderr_excerpt, session_id_after });
  appendAgentRunEvent(id, {
    agent_id: run.agent_id,
    task_id: run.task_id,
    event_type: error ? 'error' : 'lifecycle',
    level: error ? 'error' : 'info',
    message: error || `Run ${terminal}`,
    payload: { status: terminal },
  });
  if (run.task_id) {
    releaseTask(run.task_id, { clearExecution: true });
  }
  return run;
}

export function cancelAgentRun(id) {
  return markAgentRunFinished(id, { status: 'cancelled', error: 'Cancelled by user' });
}

export function getTaskActiveRun(taskId) {
  return rowToAgentRun(getDb().prepare(`
    SELECT * FROM agent_runs
    WHERE task_id = ? AND status IN ('queued', 'running', 'waiting_approval')
    ORDER BY created_at DESC
    LIMIT 1
  `).get(taskId));
}

export function getTaskLastRun(taskId) {
  return rowToAgentRun(getDb().prepare(`
    SELECT * FROM agent_runs
    WHERE task_id = ?
    ORDER BY created_at DESC
    LIMIT 1
  `).get(taskId));
}

export function getTaskLastFailure(taskId) {
  return rowToAgentRun(getDb().prepare(`
    SELECT * FROM agent_runs
    WHERE task_id = ? AND status IN ('failed', 'timed_out', 'cancelled')
    ORDER BY COALESCE(finished_at, updated_at, created_at) DESC
    LIMIT 1
  `).get(taskId));
}

export function getTaskOperatorSnapshot(taskId) {
  const task = getTask(taskId);
  if (!task) return null;
  const activeRun = getTaskActiveRun(taskId);
  const lastRun = getTaskLastRun(taskId);
  const lastFailure = getTaskLastFailure(taskId);
  const needsAction = task.status === 'blocked' || activeRun?.status === 'waiting_approval' || Boolean(lastFailure?.error);
  return {
    task_id: taskId,
    active_run_id: activeRun?.id || null,
    active_run_status: activeRun?.status || null,
    last_run_id: lastRun?.id || null,
    last_run_status: lastRun?.status || null,
    last_failure_run_id: lastFailure?.id || null,
    last_failure_status: lastFailure?.status || null,
    last_failure_error: lastFailure?.error || null,
    checkout_run_id: task.checkout_run_id || null,
    execution_run_id: task.execution_run_id || null,
    needs_action: needsAction,
    needs_action_reason: task.status === 'blocked'
      ? (task.blocked_reason || 'Task is blocked')
      : activeRun?.status === 'waiting_approval'
        ? 'Run is waiting for approval'
        : lastFailure?.error
          ? lastFailure.error
          : null,
  };
}

export function getTaskLiveRuns(taskId) {
  return listAgentRuns({ task_id: taskId }).filter(run => isActiveRunStatus(run.status));
}

export function upsertAgentRuntimeState(agentId, patch = {}) {
  const db = getDb();
  const now = nowIso();
  db.prepare(`
    INSERT INTO agent_runtime_state
      (agent_id, session_id, state_json, last_run_id, last_run_status, total_input_tokens, total_output_tokens,
       total_cached_input_tokens, total_cost_cents, last_error, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ON CONFLICT(agent_id) DO UPDATE SET
      session_id = excluded.session_id,
      state_json = COALESCE(excluded.state_json, agent_runtime_state.state_json),
      last_run_id = COALESCE(excluded.last_run_id, agent_runtime_state.last_run_id),
      last_run_status = COALESCE(excluded.last_run_status, agent_runtime_state.last_run_status),
      total_input_tokens = excluded.total_input_tokens,
      total_output_tokens = excluded.total_output_tokens,
      total_cached_input_tokens = excluded.total_cached_input_tokens,
      total_cost_cents = excluded.total_cost_cents,
      last_error = excluded.last_error,
      updated_at = excluded.updated_at
  `).run(
    agentId,
    patch.session_id || null,
    patch.state ? stringifyJson(patch.state) : null,
    patch.last_run_id || null,
    patch.last_run_status || null,
    Number(patch.total_input_tokens || 0),
    Number(patch.total_output_tokens || 0),
    Number(patch.total_cached_input_tokens || 0),
    Number(patch.total_cost_cents || 0),
    patch.last_error || null,
    now,
    now
  );
}

export function upsertAgentTaskSession({ agent_id, task_key, adapter_type = 'default', session_params = null, session_display_id = null, last_run_id = null, last_error = null } = {}) {
  if (!agent_id || !task_key) return null;
  const db = getDb();
  const now = nowIso();
  const existing = db.prepare('SELECT id FROM agent_task_sessions WHERE agent_id = ? AND task_key = ?').get(agent_id, task_key);
  const id = existing?.id || newId('atsession');
  db.prepare(`
    INSERT INTO agent_task_sessions
      (id, agent_id, task_key, adapter_type, session_params_json, session_display_id, last_run_id, last_error, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ON CONFLICT(agent_id, task_key) DO UPDATE SET
      adapter_type = excluded.adapter_type,
      session_params_json = COALESCE(excluded.session_params_json, agent_task_sessions.session_params_json),
      session_display_id = COALESCE(excluded.session_display_id, agent_task_sessions.session_display_id),
      last_run_id = COALESCE(excluded.last_run_id, agent_task_sessions.last_run_id),
      last_error = excluded.last_error,
      updated_at = excluded.updated_at
  `).run(id, agent_id, task_key, adapter_type, session_params ? stringifyJson(session_params) : null, session_display_id, last_run_id, last_error, now, now);
  return id;
}

export function createTaskRun({ task_id, agent_id, trigger_source = 'task_assigned', trigger_detail = null } = {}) {
  const task = getTask(task_id);
  if (!task) throw new Error(`Task not found: ${task_id}`);
  if (!agent_id) throw new Error('agent_id is required for task runs');
  const run = createAgentRun({ agent_id, task_id, trigger_source, trigger_detail });
  checkoutTask(task_id, { run_id: run.id });
  upsertAgentTaskSession({ agent_id, task_key: task_id, last_run_id: run.id });
  return run;
}

export function reportingSnapshot() {
  const db = getDb();
  const one = (sql) => db.prepare(sql).get();
  const many = (sql) => db.prepare(sql).all();
  return {
    tasks_by_status: many('SELECT status, COUNT(*) AS count FROM tasks GROUP BY status ORDER BY status ASC'),
    tasks_by_assignee: many('SELECT assignee_agent_id, COUNT(*) AS count FROM tasks GROUP BY assignee_agent_id ORDER BY count DESC'),
    blocked_tasks: listTasks({ status: 'blocked', limit: 100 }),
    stale_tasks: many(`
      SELECT * FROM task_lifecycle_view
      WHERE status IN ('todo', 'in_progress', 'blocked', 'in_review') AND age_seconds > 86400
      ORDER BY age_seconds DESC
    `),
    run_success_rate: many(`
      SELECT agent_id,
             SUM(CASE WHEN status = 'succeeded' THEN 1 ELSE 0 END) AS succeeded,
             COUNT(*) AS total
      FROM agent_runs
      GROUP BY agent_id
      ORDER BY total DESC
    `),
    avg_completion_time: many(`
      SELECT assignee_agent_id,
             AVG((julianday(completed_at) - julianday(created_at)) * 86400) AS avg_seconds
      FROM tasks
      WHERE completed_at IS NOT NULL
      GROUP BY assignee_agent_id
    `),
    manager_delegation: many('SELECT * FROM manager_delegation_view ORDER BY delegated_task_count DESC'),
    workload: many('SELECT * FROM agent_workload_view ORDER BY active_tasks DESC, total_tasks DESC'),
    approval_latency: many('SELECT * FROM approval_latency_view ORDER BY created_at DESC LIMIT 100'),
    run_failures: many('SELECT * FROM run_failure_view ORDER BY created_at DESC LIMIT 100'),
    totals: {
      active_agents: Number(one("SELECT COUNT(*) AS count FROM agents WHERE status != 'terminated'").count || 0),
      active_tasks: Number(one("SELECT COUNT(*) AS count FROM tasks WHERE status IN ('todo', 'in_progress', 'in_review', 'blocked')").count || 0),
      blocked_tasks: Number(one("SELECT COUNT(*) AS count FROM tasks WHERE status = 'blocked'").count || 0),
      pending_approvals: Number(one("SELECT COUNT(*) AS count FROM approval_requests WHERE status = 'pending'").count || 0),
      running_executions: Number(one("SELECT COUNT(*) AS count FROM agent_runs WHERE status IN ('queued', 'running', 'waiting_approval')").count || 0),
      recent_delegations: Number(one("SELECT COUNT(*) AS count FROM activity_log WHERE action IN ('task.assigned', 'task.reassigned') AND created_at >= datetime('now', '-7 day')").count || 0),
      recent_completions: Number(one("SELECT COUNT(*) AS count FROM tasks WHERE status = 'done' AND completed_at >= datetime('now', '-7 day')").count || 0),
    },
  };
}
