import type { FastMCP } from 'fastmcp';
import { z } from 'zod';

import { getController } from '../controller';
import { assertConfiguredTeam } from '../utils/teamConfig';
import { jsonTextContent } from '../utils/format';

const toolContextSchema = {
  teamName: z.string().min(1),
  claudeDir: z.string().min(1).optional(),
};

export function registerMessageTools(server: Pick<FastMCP, 'addTool'>) {
  server.addTool({
    name: 'message_send',
    description:
      'Send a visible team/user message into team inbox. OpenCode teammates should use this for normal replies to the human user, lead, or same-team teammates. from is required and must be your configured teammate name; user is reserved for app-owned writes. When replying to an app-delivered OpenCode runtime message, include source="runtime_delivery" and relayOfMessageId with the inbound app messageId. After a successful app-delivered runtime reply, stop and do not send the same answer again. Do not invent placeholder task refs. If the message is not about a real board task, omit # task labels; never use #00000000.',
    parameters: z.object({
      ...toolContextSchema,
      to: z.string().min(1),
      text: z.string().min(1),
      from: z.string().min(1),
      summary: z.string().optional(),
      source: z.string().optional(),
      relayOfMessageId: z.string().optional(),
      leadSessionId: z.string().optional(),
      attachments: z
        .array(
          z.object({
            id: z.string().min(1),
            filename: z.string().min(1),
            mimeType: z.string().min(1),
            size: z.number().nonnegative(),
          })
        )
        .optional(),
      taskRefs: z
        .array(
          z.object({
            taskId: z.string().min(1),
            displayId: z.string().min(1),
            teamName: z.string().min(1),
          })
        )
        .optional(),
    }),
    execute: async ({
      teamName,
      claudeDir,
      to,
      text,
      from,
      summary,
      source,
      relayOfMessageId,
      leadSessionId,
      attachments,
      taskRefs,
    }) => {
      assertConfiguredTeam(teamName, claudeDir);
      const result = getController(teamName, claudeDir).messages.sendMessage({
        to,
        text,
        ...(from ? { from } : {}),
        ...(summary ? { summary } : {}),
        ...(source ? { source } : {}),
        ...(relayOfMessageId ? { relayOfMessageId } : {}),
        ...(leadSessionId ? { leadSessionId } : {}),
        ...(attachments?.length ? { attachments } : {}),
        ...(taskRefs?.length ? { taskRefs } : {}),
      });
      const protocolInstruction =
        source === 'runtime_delivery' || relayOfMessageId
          ? 'Delivered as an app-delivered runtime reply. Stop this turn now; do not call message_send again for the same inbound message.'
          : 'Delivered. If this answered one app/user instruction, do not call message_send again for the same answer.';
      const payload =
        result && typeof result === 'object' && !Array.isArray(result)
          ? { ...(result as Record<string, unknown>), protocolInstruction }
          : { result, protocolInstruction };
      return await Promise.resolve(jsonTextContent(payload));
    },
  });
}
