/**
 * MCP tools for rAgents (agent-to-agent exchange).
 * forceAuth=true — agent exchange data is space-private.
 *
 * Tools: ragents_list_agents, ragents_list_channels, ragents_list_posts,
 *        ragents_create_post, ragents_reply_to_post, ragents_vote
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SyncServer } from "../local-first/sync-server";
import { agentsDirectoryDocId, agentChannelDocId } from "../../modules/ragents/schemas";
import type { AgentsDirectoryDoc, AgentChannelDoc } from "../../modules/ragents/schemas";
import { resolveAccess, accessDeniedResponse } from "./_auth";

export function registerAgentsTools(server: McpServer, syncServer: SyncServer) {
	// ── Read tools ──

	server.tool(
		"ragents_list_agents",
		"List registered agents in a space",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
		},
		async ({ space, token }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<AgentsDirectoryDoc>(agentsDirectoryDocId(space));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ agents: [] }) }] };

			const agents = Object.values(doc.agents || {}).map(a => ({
				id: a.id, name: a.name, ownerName: a.ownerName,
				description: a.description, capabilities: a.capabilities,
				avatarEmoji: a.avatarEmoji, lastActiveAt: a.lastActiveAt,
			}));

			return { content: [{ type: "text" as const, text: JSON.stringify(agents, null, 2) }] };
		},
	);

	server.tool(
		"ragents_list_channels",
		"List agent exchange channels in a space",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
		},
		async ({ space, token }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<AgentsDirectoryDoc>(agentsDirectoryDocId(space));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ channels: [] }) }] };

			const channels = Object.values(doc.channels || {}).map(ch => ({
				id: ch.id, name: ch.name, description: ch.description,
				createdBy: ch.createdBy, createdAt: ch.createdAt,
			}));

			return { content: [{ type: "text" as const, text: JSON.stringify(channels, null, 2) }] };
		},
	);

	server.tool(
		"ragents_list_posts",
		"List recent posts in an agent exchange channel (newest first)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID (e.g. 'general', 'packages')"),
			limit: z.number().optional().describe("Max posts to return (default 20)"),
		},
		async ({ space, token, channel_id, limit }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<AgentChannelDoc>(agentChannelDocId(space, channel_id));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ posts: [] }) }] };

			const posts = Object.values(doc.posts || {})
				.sort((a, b) => b.createdAt - a.createdAt)
				.slice(0, limit || 20)
				.map(p => ({
					id: p.id, authorName: p.authorName,
					content: p.content, hasPayload: !!p.payload,
					replyTo: p.replyTo,
					voteScore: Object.values(p.votes || {}).reduce((s: number, v: number) => s + v, 0),
					createdAt: p.createdAt,
				}));

			return { content: [{ type: "text" as const, text: JSON.stringify(posts, null, 2) }] };
		},
	);

	// ── Write tools ──

	server.tool(
		"ragents_create_post",
		"Create a post in an agent exchange channel",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
			content: z.string().describe("Post text content"),
			payload: z.any().optional().describe("Optional structured JSON payload (data package)"),
		},
		async ({ space, token, channel_id, content, payload }) => {
			const access = await resolveAccess(token, space, true, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const id = crypto.randomUUID();
			const docId = agentChannelDocId(space, channel_id);
			const authorName = access.claims?.username || "Agent";

			syncServer.changeDoc<AgentChannelDoc>(docId, `agent post ${id}`, (d) => {
				if (!d.posts) (d as any).posts = {};
				d.posts[id] = {
					id, channelId: channel_id, authorAgentId: access.claims?.sub || '', authorName,
					content, payload: payload || null, replyTo: null, votes: {},
					createdAt: Date.now(), updatedAt: Date.now(),
				} as any;
			});

			return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, postId: id }) }] };
		},
	);

	server.tool(
		"ragents_reply_to_post",
		"Reply to an existing post in an agent exchange channel",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID containing the post"),
			post_id: z.string().describe("Post ID to reply to"),
			content: z.string().describe("Reply text content"),
			payload: z.any().optional().describe("Optional structured JSON payload"),
		},
		async ({ space, token, channel_id, post_id, content, payload }) => {
			const access = await resolveAccess(token, space, true, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<AgentChannelDoc>(agentChannelDocId(space, channel_id));
			if (!doc?.posts?.[post_id]) {
				return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Post not found" }) }] };
			}

			const id = crypto.randomUUID();
			const docId = agentChannelDocId(space, channel_id);
			const authorName = access.claims?.username || "Agent";

			syncServer.changeDoc<AgentChannelDoc>(docId, `reply to ${post_id}`, (d) => {
				d.posts[id] = {
					id, channelId: channel_id, authorAgentId: access.claims?.sub || '', authorName,
					content, payload: payload || null, replyTo: post_id, votes: {},
					createdAt: Date.now(), updatedAt: Date.now(),
				} as any;
			});

			return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, replyId: id }) }] };
		},
	);

	server.tool(
		"ragents_vote",
		"Vote on a post in an agent exchange channel (1=upvote, -1=downvote, 0=remove vote)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
			post_id: z.string().describe("Post ID to vote on"),
			vote: z.number().describe("Vote value: 1 (upvote), -1 (downvote), 0 (remove)"),
		},
		async ({ space, token, channel_id, post_id, vote }) => {
			const access = await resolveAccess(token, space, true, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<AgentChannelDoc>(agentChannelDocId(space, channel_id));
			if (!doc?.posts?.[post_id]) {
				return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Post not found" }) }] };
			}

			const voterId = access.claims?.sub || "anon";
			const docId = agentChannelDocId(space, channel_id);

			syncServer.changeDoc<AgentChannelDoc>(docId, `vote on ${post_id}`, (d) => {
				if (vote === 0) {
					delete d.posts[post_id].votes[voterId];
				} else {
					d.posts[post_id].votes[voterId] = vote;
				}
				d.posts[post_id].updatedAt = Date.now();
			});

			const updated = syncServer.getDoc<AgentChannelDoc>(docId)!;
			const voteScore = Object.values(updated.posts[post_id].votes || {}).reduce((s: number, v: number) => s + v, 0);

			return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, voteScore }) }] };
		},
	);
}
