// v2.x: Background subagent tools. Three tools that let the model spawn
// non-blocking subagent tasks, poll their status, and retrieve results.
//
//   spawn_subagent  — Create a background session+chat, dispatch inference,
//                     return immediately with a task_id.
//   subagent_status — Poll the status of a previously spawned task.
//   subagent_result — Retrieve the full output of a completed task.
//
// These tools reuse the existing sessions/chats/messages/tables and the
// inference pipeline — no new tables or services needed.
//
// Registered in tools.ts ALL_TOOLS. Lives in its own file so tests can
// import executors without dragging in the full tool registry.
//
// Follows the read_tab_by_number.ts pattern: a pure executor function plus
// a ToolDef wrapper. Type-only import from tools.ts to dodge runtime cycles.

import { z } from 'zod';
import type { Sql } from '../../db.js';
import type { ToolDef, ToolExecCtx } from '../tools.js';
import {
  spawnBackgroundTask,
  getBackgroundTaskStatus,
  getBackgroundTaskResult,
} from '../background-task.js';

export const SpawnSubagentInput = z.object({
  input: z.string().min(1).describe('The task to execute in the background'),
  model: z
    .string()
    .min(1)
    .optional()
    .describe('Model to use (defaults to session model)'),
  agent: z
    .string()
    .min(1)
    .optional()
    .describe('Agent to use (defaults to boocode)'),
  label: z
    .string()
    .max(100)
    .optional()
    .describe('Human-readable label for display'),
});

export type SpawnSubagentInputT = z.infer<typeof SpawnSubagentInput>;

export async function executeSpawnSubagent(
  input: SpawnSubagentInputT,
  sql: Sql,
  sessionId: string,
): Promise<Record<string, unknown>> {
  // Resolve project_id + model from the current session.
  const sessRows = await sql<
    { project_id: string; model: string }[]
  >`
    SELECT project_id, model FROM sessions WHERE id = ${sessionId}
  `;
  if (sessRows.length === 0) {
    return { error: 'current session not found' };
  }
  const projectId = sessRows[0]!.project_id;
  const model = input.model ?? sessRows[0]!.model;

  const task = await spawnBackgroundTask(
    sql,
    // We pass a minimal logger shim — the real logger is wired by the
    // inference pipeline. This keeps the tool's execute signature clean.
    { info: () => {}, warn: () => {}, error: () => {} } as unknown as import('fastify').FastifyBaseLogger,
    projectId,
    input.input,
    model,
    input.agent,
    input.label,
  );

  // Elapsed time since creation is negligible (task was just spawned).
  return {
    task_id: task.id,
    status: task.status,
    session_id: task.session_id,
    chat_id: task.chat_id,
    created_at: task.created_at,
  };
}

export const spawnSubagent: ToolDef<SpawnSubagentInputT> = {
  name: 'spawn_subagent',
  description:
    'Spawn a background subagent task. Creates a new session and chat, dispatches inference asynchronously, and returns immediately with a task_id. Use subagent_status to poll for completion and subagent_result to retrieve the full output. Non-blocking — the model continues while the subagent works in the background.',
  inputSchema: SpawnSubagentInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'spawn_subagent',
      description:
        'Spawn a background subagent task. Returns immediately with a task_id — poll with subagent_status.',
      parameters: {
        type: 'object',
        properties: {
          input: {
            type: 'string',
            description: 'The task to execute in the background',
          },
          model: {
            type: 'string',
            description: 'Model to use (defaults to session model)',
          },
          agent: {
            type: 'string',
            description: 'Agent to use (defaults to boocode)',
          },
          label: {
            type: 'string',
            maxLength: 100,
            description: 'Human-readable label for display',
          },
        },
        required: ['input'],
        additionalProperties: false,
      },
    },
  },
  async execute(input, _projectRoot, _extraRoots, toolCtx?: ToolExecCtx) {
    if (!toolCtx) {
      return { error: 'spawn_subagent unavailable: no session context' };
    }
    try {
      return await executeSpawnSubagent(input, toolCtx.sql, toolCtx.sessionId);
    } catch (err) {
      return {
        error: `spawn_subagent failed: ${err instanceof Error ? err.message : String(err)}`,
      };
    }
  },
};

export const SubagentStatusInput = z.object({
  task_id: z.string().uuid().describe('Task ID from spawn_subagent'),
});

export type SubagentStatusInputT = z.infer<typeof SubagentStatusInput>;

export async function executeSubagentStatus(
  input: SubagentStatusInputT,
  sql: Sql,
): Promise<Record<string, unknown>> {
  const task = await getBackgroundTaskStatus(sql, input.task_id);
  if (!task) {
    return { error: 'task not found', task_id: input.task_id };
  }

  // Compute elapsed time from created_at (ISO string).
  let elapsed_seconds: number | null = null;
  try {
    const created = new Date(task.created_at).getTime();
    const finished = task.finished_at
      ? new Date(task.finished_at).getTime()
      : Date.now();
    elapsed_seconds = Math.round((finished - created) / 1000);
  } catch {
    elapsed_seconds = null;
  }

  return {
    task_id: task.id,
    status: task.status,
    output_summary: task.output_summary,
    finished_at: task.finished_at,
    elapsed_seconds,
  };
}

export const subagentStatus: ToolDef<SubagentStatusInputT> = {
  name: 'subagent_status',
  description:
    'Poll the status of a background subagent task by task_id. Returns the current status (running/completed/failed/cancelled), an output summary if completed, and elapsed time. Useful after spawn_subagent to check if work is done.',
  inputSchema: SubagentStatusInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'subagent_status',
      description:
        'Poll the status of a background subagent task. Returns status, output summary, and elapsed time.',
      parameters: {
        type: 'object',
        properties: {
          task_id: {
            type: 'string',
            format: 'uuid',
            description: 'Task ID from spawn_subagent',
          },
        },
        required: ['task_id'],
        additionalProperties: false,
      },
    },
  },
  async execute(input, _projectRoot, _extraRoots, toolCtx?: ToolExecCtx) {
    if (!toolCtx) {
      return { error: 'subagent_status unavailable: no session context' };
    }
    try {
      return await executeSubagentStatus(input, toolCtx.sql);
    } catch (err) {
      return {
        error: `subagent_status failed: ${err instanceof Error ? err.message : String(err)}`,
      };
    }
  },
};

export const SubagentResultInput = z.object({
  task_id: z.string().uuid().describe('Task ID from spawn_subagent'),
});

export type SubagentResultInputT = z.infer<typeof SubagentResultInput>;

export async function executeSubagentResult(
  input: SubagentResultInputT,
  sql: Sql,
): Promise<Record<string, unknown>> {
  const task = await getBackgroundTaskStatus(sql, input.task_id);
  if (!task) {
    return { error: 'task not found', task_id: input.task_id };
  }

  if (task.status !== 'completed') {
    return {
      task_id: task.id,
      status: task.status,
      error: `task is not yet completed (status: ${task.status})`,
    };
  }

  if (!task.chat_id) {
    return { error: 'task has no chat data', task_id: input.task_id };
  }

  const result = await getBackgroundTaskResult(sql, input.task_id, task.chat_id);
  if (!result) {
    return {
      task_id: task.id,
      status: task.status,
      error: 'task completed but no output message found',
    };
  }

  return {
    task_id: task.id,
    output: result.output,
    token_usage: result.token_usage,
  };
}

export const subagentResult: ToolDef<SubagentResultInputT> = {
  name: 'subagent_result',
  description:
    'Retrieve the full output of a completed background subagent task by task_id. Returns the response text and token usage. The task must be in completed status — poll with subagent_status first.',
  inputSchema: SubagentResultInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'subagent_result',
      description:
        'Retrieve the full output of a completed background subagent task. Returns output text and token usage.',
      parameters: {
        type: 'object',
        properties: {
          task_id: {
            type: 'string',
            format: 'uuid',
            description: 'Task ID from spawn_subagent',
          },
        },
        required: ['task_id'],
        additionalProperties: false,
      },
    },
  },
  async execute(input, _projectRoot, _extraRoots, toolCtx?: ToolExecCtx) {
    if (!toolCtx) {
      return { error: 'subagent_result unavailable: no session context' };
    }
    try {
      return await executeSubagentResult(input, toolCtx.sql);
    } catch (err) {
      return {
        error: `subagent_result failed: ${err instanceof Error ? err.message : String(err)}`,
      };
    }
  },
};
