/**
 * MCP tools for rChats (multiplayer chat channels).
 * forceAuth=true — chat messages are always sensitive.
 *
 * Tools: rchats_list_channels, rchats_get_channel, rchats_list_messages,
 *        rchats_list_thread_messages, rchats_list_dms, rchats_send_message
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SyncServer } from "../local-first/sync-server";
import { chatsDirectoryDocId, chatChannelDocId, dmChannelDocId } from "../../modules/rchats/schemas";
import type { ChatsDirectoryDoc, ChatChannelDoc } from "../../modules/rchats/schemas";
import { resolveAccess, accessDeniedResponse } from "./_auth";
import { filterArrayByVisibility } from "../../shared/membrane";

export function registerChatsTools(server: McpServer, syncServer: SyncServer) {
	server.tool(
		"rchats_list_channels",
		"List chat channels in a space",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token (required — chat data is private)"),
		},
		async ({ space, token }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

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

			const channels = filterArrayByVisibility(Object.values(doc.channels || {}), access.role).map(ch => ({
				id: ch.id, name: ch.name, description: ch.description,
				isPrivate: ch.isPrivate, isDm: ch.isDm,
				createdBy: ch.createdBy,
				createdAt: ch.createdAt, updatedAt: ch.updatedAt,
			}));

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

	server.tool(
		"rchats_get_channel",
		"Get channel details including members",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
		},
		async ({ space, token, channel_id }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<ChatChannelDoc>(chatChannelDocId(space, channel_id));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Channel not found" }) }] };

			const members = Object.values(doc.members || {});
			const messageCount = Object.keys(doc.messages || {}).length;

			return {
				content: [{
					type: "text" as const,
					text: JSON.stringify({ channelId: doc.channelId, members, messageCount, isDm: doc.isDm }, null, 2),
				}],
			};
		},
	);

	server.tool(
		"rchats_list_messages",
		"List recent messages in a chat channel (newest first)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
			limit: z.number().optional().describe("Max messages to return (default 50)"),
		},
		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<ChatChannelDoc>(chatChannelDocId(space, channel_id));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Channel not found" }) }] };

			let messages = filterArrayByVisibility(Object.values(doc.messages || {}), access.role)
				.filter(m => !m.threadId) // Only top-level messages
				.sort((a, b) => b.createdAt - a.createdAt)
				.slice(0, limit || 50);

			const result = messages.map(m => ({
				id: m.id, authorName: m.authorName,
				content: m.content, replyTo: m.replyTo,
				reactions: m.reactions || {},
				editedAt: m.editedAt, createdAt: m.createdAt,
			}));

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

	server.tool(
		"rchats_list_thread_messages",
		"List messages in a thread (replies to a root message)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
			thread_id: z.string().describe("Root message ID of the thread"),
		},
		async ({ space, token, channel_id, thread_id }) => {
			const access = await resolveAccess(token, space, false, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<ChatChannelDoc>(chatChannelDocId(space, channel_id));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Channel not found" }) }] };

			const rootMsg = doc.messages?.[thread_id];
			const replies = filterArrayByVisibility(
				Object.values(doc.messages || {}).filter(m => m.threadId === thread_id),
				access.role,
			).sort((a, b) => a.createdAt - b.createdAt);

			const result = {
				rootMessage: rootMsg ? { id: rootMsg.id, authorName: rootMsg.authorName, content: rootMsg.content, createdAt: rootMsg.createdAt } : null,
				replies: replies.map(m => ({
					id: m.id, authorName: m.authorName,
					content: m.content, createdAt: m.createdAt,
				})),
				replyCount: replies.length,
			};

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

	server.tool(
		"rchats_list_dms",
		"List DM channels for the authenticated user",
		{
			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<ChatsDirectoryDoc>(chatsDirectoryDocId(space));
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ dms: [] }) }] };

			const userDid = String(access.claims?.did || access.claims?.sub || '');
			const dms = Object.values(doc.channels || {})
				.filter(ch => ch.isDm && ch.id.includes(userDid))
				.map(ch => ({ id: ch.id, createdAt: ch.createdAt }));

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

	server.tool(
		"rchats_send_message",
		"Send a message to a chat channel (write operation — requires auth token)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			channel_id: z.string().describe("Channel ID"),
			content: z.string().describe("Message content"),
		},
		async ({ space, token, channel_id, content }) => {
			const access = await resolveAccess(token, space, true, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const docId = chatChannelDocId(space, channel_id);
			let doc = syncServer.getDoc<ChatChannelDoc>(docId);
			if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Channel not found" }) }] };

			const id = crypto.randomUUID();
			const authorDid = String(access.claims?.did || access.claims?.sub || '');
			const authorName = String(access.claims?.username || 'MCP');
			syncServer.changeDoc<ChatChannelDoc>(docId, `mcp send message ${id}`, (d) => {
				d.messages[id] = {
					id, channelId: channel_id,
					authorId: authorDid,
					authorName,
					content, replyTo: null,
					reactions: {}, transclusions: [],
					editedAt: null, createdAt: Date.now(),
				};
			});

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