import { Type } from '@sinclair/typebox';
import type { AnyAgentTool } from '../common.js';
import { textResult } from '../common.js';
import type { CrewRunContext, ParallelTaskItem } from '../../service/crew_types.js';
import type { NetaClawSubagentTask } from '../../service/subagent.js';
import type { SessionDelegateToolContext } from '../../service/tool_resolver.js';

const DelegateParallelTaskItemParams = Type.Object({
  agent_name: Type.Optional(Type.String({ description: 'Crew member agent name' })),
  task_description: Type.Optional(Type.String({ description: 'Crew task description' })),
  context: Type.Optional(Type.String({ description: 'Optional task context' })),
  goal: Type.Optional(Type.String({ description: 'Session subagent goal' })),
  mode: Type.Optional(Type.Literal('preset')),
  agentName: Type.Optional(Type.String({ description: 'Session subagent display name' })),
  agentId: Type.Optional(Type.Number({ description: 'Preset subagent agent id' })),
  toolNames: Type.Optional(Type.Array(Type.String(), { description: 'Requested tool allow-list for the subagent' })),
  workspaceRoots: Type.Optional(Type.Array(Type.String(), { description: 'Additional workspace roots allowed for the session subagent task' })),
});

const DelegateParallelParams = Type.Object({
  tasks: Type.Array(
    DelegateParallelTaskItemParams,
    { description: 'Task list to run in parallel' },
  ),
});

type DelegateParallelToolContext = CrewRunContext | SessionDelegateToolContext;

function isSessionDelegateToolContext(ctx: DelegateParallelToolContext): ctx is SessionDelegateToolContext {
  return (ctx as SessionDelegateToolContext).kind === 'session-subagent';
}

function normalizeSessionTask(task: Record<string, any>): NetaClawSubagentTask {
  return {
    goal: task.goal ?? task.task_description ?? '',
    context: task.context,
    mode: task.mode,
    agentName: task.agentName ?? task.agent_name,
    agentId: task.agentId ?? null,
    toolNames: Array.isArray(task.toolNames) ? task.toolNames.filter(Boolean) : undefined,
    workspaceRoots: Array.isArray(task.workspaceRoots) ? task.workspaceRoots.filter(Boolean) : undefined,
  };
}

function wrapSessionDelegateResults(tasks: NetaClawSubagentTask[], results: unknown) {
  const normalizedTasks = tasks.map((task, index) => ({
    sortOrder: index,
    goal: task.goal,
    context: task.context,
    mode: task.mode,
    agentName: task.agentName,
    agentId: task.agentId ?? null,
    toolNames: task.toolNames,
    workspaceRoots: task.workspaceRoots,
  }));
  const normalizedResults = Array.isArray(results) ? results : [];

  return {
    delegationMode: 'session-subagent',
    relayPolicy: {
      preserveExactValues: true,
      noParaphrase: true,
      noNormalization: true,
      instructions: [
        'Use the delegated results as the source of truth.',
        'Read `taskResults` from top to bottom and summarize each entry independently.',
        'Each `taskResults[i]` already binds one task to exactly one delegated result.',
        'Do not mix evidence across different `taskResults` entries.',
        'Do not rewrite filenames, paths, counts, IDs, or other enumerated values.',
        'If you present delegated items to the user, copy them exactly from the paired `taskResults[i].result`.',
      ],
    },
    taskResults: normalizedTasks.map((task, index) => ({
      sortOrder: index,
      task,
      result: normalizedResults[index],
    })),
  };
}

export function createDelegateParallelTool(ctx: DelegateParallelToolContext): AnyAgentTool {
  return {
    name: 'delegate_parallel',
    label: 'Delegate Parallel',
    description: 'Delegate multiple tasks in parallel and return all structured results.',
    parameters: DelegateParallelParams,
    visibility: 'internal',
    async execute(id, params) {
      const { tasks } = params as { tasks: Array<ParallelTaskItem & Record<string, any>> };

      if (isSessionDelegateToolContext(ctx)) {
        const normalizedTasks = tasks.map(task => normalizeSessionTask(task));
        const invalidTask = normalizedTasks.find(task => !task.goal.trim());
        if (invalidTask) {
          return textResult(JSON.stringify({
            status: 'failed',
            error: 'Each task requires goal or task_description',
          }));
        }

        const results = await ctx.runBatch(normalizedTasks, {
          parentToolCallId: id,
          mode: 'parallel',
        });

        return textResult(JSON.stringify(wrapSessionDelegateResults(normalizedTasks, results)));
      }

      const normalizedTasks = tasks.map(task => ({
        agent_name: task.agent_name ?? task.agentName,
        task_description: task.task_description ?? task.goal,
        context: task.context,
      }));

      const invalidTask = normalizedTasks.find(task => !task.agent_name || !task.task_description);
      if (invalidTask) {
        return textResult(JSON.stringify({
          status: 'failed',
          error: 'Each task requires agent_name/agentName and task_description/goal',
        }));
      }

      const missing: string[] = [];
      for (const task of normalizedTasks) {
        if (!ctx.memberAgents.find(member => member.name === task.agent_name)) {
          missing.push(task.agent_name!);
        }
      }

      if (missing.length > 0) {
        const available = ctx.memberAgents.map(member => member.name).join(', ');
        return textResult(JSON.stringify({
          status: 'failed',
          error: `Unknown agents: ${missing.join(', ')}. Available agents: ${available}`,
        }));
      }

      const delegate = (ctx as any)._delegate;
      const results = await delegate.executeParallel(ctx, normalizedTasks);

      return textResult(JSON.stringify(results));
    },
  };
}

import { registerSchema } from '../catalog.js';
registerSchema({
  name: 'delegate_parallel',
  toolset: 'crew',
  description: 'Delegate multiple tasks to crew members or session subagents in parallel',
  capability: 'text',
  visibility: 'internal',
  isCore: true,
  canDisable: false,
});
