/**
 * RequestTool — META_TOOL for the tool-request mechanism.
 *
 * When an agent realises it needs a tool that this movement does not expose,
 * it calls RequestTool({ name, reason }). The request is recorded so the gap
 * becomes visible in the task detail and per-piece aggregation, so an operator
 * can enable the tool for the workspace (Settings → Tools). When the run can
 * reach a user, the interactive approval path (`toolApprovalInteractive`)
 * pauses for consent and lets the run continue with the tool once approved;
 * otherwise the request is recorded and the agent proceeds without it.
 *
 * Classification (recorded as `category`):
 *   - already_available → the tool IS exposed here; just call it (not recorded).
 *   - available_not_allowed (`requested`) → exists in the catalog but not in
 *     this movement → recorded as a genuine omission candidate.
 *   - unknown → not in the catalog (possible hallucination / capability gap).
 *
 * Plumbing: ToolContext carries `effectiveToolNames` (this movement's set),
 * `knownToolNames` (full catalog, injected by the dispatcher), and
 * `recordToolRequest` (bound to task/job/space by the worker).
 */

import { ToolDef } from '../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from './core.js';

const REQUEST_TOOL_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'RequestTool',
    description:
      'この movement に無いツールが必要なとき name と理由(reason)を渡して要求する。ユーザーが応答できる実行では承認を求め、承認されればそのまま使える。それ以外は記録のみで即時には使えないので、無しで進めるか complete({status:"needs_user_input"}) でユーザーに依頼する（恒久的に許可するにはワークスペースの設定 → ツールで有効化）。詳細は ReadToolDoc({ name: "RequestTool" })。',
    parameters: {
      type: 'object',
      properties: {
        name: { type: 'string', description: '必要なツール名（例: WebSearch, Bash）。' },
        reason: { type: 'string', description: 'なぜそのツールが必要か（具体的に）。' },
      },
      required: ['name', 'reason'],
    },
  },
};

export const TOOL_DEFS: Record<string, ToolDef> = {
  RequestTool: REQUEST_TOOL_DEF,
};

export async function executeTool(
  name: string,
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult | null> {
  if (name !== 'RequestTool') return null;

  const toolName = typeof input['name'] === 'string' ? input['name'].trim() : '';
  const reason = typeof input['reason'] === 'string' ? input['reason'].trim() : '';
  if (!toolName) return { output: 'RequestTool: name は必須です。', isError: true };
  if (!reason) return { output: 'RequestTool: reason（なぜ必要か）は必須です。', isError: true };

  // Already available in THIS movement → nothing to record, just tell it to call.
  const effective = ctx.effectiveToolNames ?? [];
  if (effective.includes(toolName)) {
    return {
      output: `"${toolName}" はこの movement で既に利用可能です。そのまま呼び出してください。`,
      isError: false,
    };
  }

  // Classify against the full catalog. MCP tools (mcp__*) can't be cheaply
  // enumerated, so treat them as "exists but not allowed here".
  const known = ctx.knownToolNames ?? [];
  const isMcp = toolName.startsWith('mcp__');
  const category: 'requested' | 'unknown' = known.includes(toolName) || isMcp ? 'requested' : 'unknown';

  if (category === 'unknown') {
    // The requested tool does not exist in the catalog (likely a hallucinated
    // name). Return a HARD ERROR so the agent does not mistake it for a pending
    // approval or a successful call. Still recorded for diagnostics (only when a
    // recorder is wired — `?.` is a silent no-op otherwise, so the message must
    // NOT claim it was recorded).
    ctx.recordToolRequest?.({ toolName, reason, category: 'unknown' });
    return {
      output:
        `"${toolName}" というツールは存在しません。RequestTool で要求できるのは実在するツール名だけです。` +
        ` これは承認待ちではありません — 利用可能なツールで進めてください（一覧は ReadToolDoc 等で確認できます）。`,
      isError: true,
    };
  }

  // available_not_allowed. If this run can reach a user, pause for inline
  // approval (blocking permission-prompt). Otherwise record auto_denied and
  // let the agent proceed without it (the user finds the request later).
  if (ctx.toolApprovalInteractive) {
    ctx.recordToolRequest?.({ toolName, reason, category: 'requested', status: 'pending' });
    ctx.pendingToolApproval = { toolName };
    return {
      output:
        `"${toolName}" の利用にはユーザーの承認が必要です。承認/拒否を待っています。` +
        ` 承認されればこのまま続行でき、拒否されればそのツール無しで進めることになります。`,
      isError: false,
    };
  }
  ctx.recordToolRequest?.({ toolName, reason, category: 'requested', status: 'auto_denied' });
  return {
    output:
      `"${toolName}" はこの movement では許可されていないため使えません（要求を記録しました）。` +
      ` このツール無しで進めるか、達成できない場合は complete({status:"needs_user_input", missing_info:"..."}) でユーザーに依頼してください。` +
      ` ワークスペースの設定（設定 → ツール）で許可すると次回から使えます。`,
    isError: false,
  };
}
