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

const DelegateTaskParams = 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' })),
});

type DelegateTaskToolContext = CrewRunContext | SessionDelegateToolContext;

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

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

function wrapSessionDelegateResult(result: unknown) {
  return {
    delegationMode: 'session-subagent',
    relayPolicy: {
      preserveExactValues: true,
      noParaphrase: true,
      noNormalization: true,
      instructions: [
        'Use the delegated result as the source of truth.',
        'Do not rewrite filenames, paths, counts, IDs, or other enumerated values.',
        'If you present delegated items to the user, copy them exactly from `result`.',
      ],
    },
    result,
  };
}

export function createDelegateTaskTool(ctx: DelegateTaskToolContext): AnyAgentTool {
  return {
    name: 'delegate_task',
    label: 'Delegate Task',
    description: 'Delegate one task to a subagent or crew member and return the structured result.',
    parameters: DelegateTaskParams,
    visibility: 'internal',
    async execute(id, params) {
      if (isSessionDelegateToolContext(ctx)) {
        const task = normalizeSessionTask(params);
        if (!task.goal.trim()) {
          return textResult(JSON.stringify({
            status: 'failed',
            error: 'goal or task_description is required',
          }));
        }

        const result = await ctx.runSingle(task, {
          parentToolCallId: id,
        });

        return textResult(JSON.stringify(wrapSessionDelegateResult(result)));
      }

      const agentName = params.agent_name ?? params.agentName;
      const taskDescription = params.task_description ?? params.goal;
      const context = params.context;

      if (!agentName || !taskDescription) {
        return textResult(JSON.stringify({
          status: 'failed',
          error: 'agent_name/agentName and task_description/goal are required',
        }));
      }

      const member = ctx.memberAgents.find(item => item.name === agentName);
      if (!member) {
        const available = ctx.memberAgents.map(item => item.name).join(', ');
        return textResult(JSON.stringify({
          status: 'failed',
          error: `Unknown agent "${agentName}". Available agents: ${available}`,
        }));
      }

      const delegate = (ctx as any)._delegate;
      const result = await delegate.executeSubAgent(
        ctx,
        member,
        taskDescription,
        context,
      );

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

import { registerSchema } from '../catalog.js';
registerSchema({
  name: 'delegate_task',
  toolset: 'crew',
  description: 'Delegate a task to a crew member or session subagent',
  capability: 'text',
  visibility: 'internal',
  isCore: true,
  canDisable: false,
});
