/**
 * MCP tools for rTime (commitments, woven tasks, external time logs).
 *
 * Tools: rtime_list_commitments, rtime_list_woven_tasks, rtime_place_task,
 *        rtime_list_time_logs, rtime_create_commitment
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SyncServer } from "../local-first/sync-server";
import {
	commitmentsDocId,
	weavingDocId,
	externalTimeLogsDocId,
} from "../../modules/rtime/schemas";
import type {
	CommitmentsDoc,
	WeavingDoc,
	ExternalTimeLogsDoc,
	Skill,
} from "../../modules/rtime/schemas";
import { boardDocId } from "../../modules/rtasks/schemas";
import type { BoardDoc } from "../../modules/rtasks/schemas";
import { resolveAccess, accessDeniedResponse } from "./_auth";
import { filterArrayByVisibility } from "../../shared/membrane";

const VALID_SKILLS: Skill[] = ["facilitation", "design", "tech", "outreach", "logistics"];

export function registerTimeTools(server: McpServer, syncServer: SyncServer) {
	server.tool(
		"rtime_list_commitments",
		"List resource commitments in a space (time pledges by members)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().optional().describe("JWT auth token"),
			skill: z.string().optional().describe("Filter by skill (facilitation, design, tech, outreach, logistics)"),
			status: z.string().optional().describe("Filter by status (active, matched, settled, withdrawn)"),
			limit: z.number().optional().describe("Max results (default 50)"),
		},
		async ({ space, token, skill, status, limit }) => {
			const access = await resolveAccess(token, space, false);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<CommitmentsDoc>(commitmentsDocId(space));
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No commitments data found" }) }] };
			}

			let items = filterArrayByVisibility(Object.values(doc.items || {}), access.role);
			if (skill) items = items.filter(c => c.skill === skill);
			if (status) items = items.filter(c => (c.status || "active") === status);

			items.sort((a, b) => b.createdAt - a.createdAt);
			items = items.slice(0, limit || 50);

			const summary = items.map(c => ({
				id: c.id,
				memberName: c.memberName,
				hours: c.hours,
				skill: c.skill,
				desc: c.desc,
				status: c.status || "active",
				createdAt: c.createdAt,
			}));

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

	server.tool(
		"rtime_list_woven_tasks",
		"List tasks placed on the rTime weaving canvas with overlay data (needs, position, connections)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().optional().describe("JWT auth token"),
			limit: z.number().optional().describe("Max results (default 50)"),
		},
		async ({ space, token, limit }) => {
			const access = await resolveAccess(token, space, false);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const wDoc = syncServer.getDoc<WeavingDoc>(weavingDocId(space));
			if (!wDoc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No weaving data found" }) }] };
			}

			// Look up rTasks board for task titles
			const boardSlug = wDoc.boardSlug || space;
			const board = syncServer.getDoc<BoardDoc>(boardDocId(space, boardSlug));

			const tasks = Object.entries(wDoc.weavingOverlays || {})
				.slice(0, limit || 50)
				.map(([id, ov]) => {
					const item = board?.tasks[id];
					return {
						id,
						title: item?.title || id,
						status: item?.status || 'TODO',
						description: item?.description || '',
						needs: ov.needs,
						canvasX: ov.canvasX,
						canvasY: ov.canvasY,
						notes: ov.notes,
						links: ov.links,
						connectionCount: Object.values(wDoc.connections || {}).filter(c => c.toTaskId === id).length,
					};
				});

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

	server.tool(
		"rtime_place_task",
		"Place an rTasks item onto the weaving canvas with skill needs (requires auth token)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			task_id: z.string().describe("rTasks task ID to place on canvas"),
			needs: z.record(z.number()).describe("Skill-to-hours map (e.g. {tech: 4, design: 2})"),
			canvas_x: z.number().optional().describe("Canvas X position (default 400)"),
			canvas_y: z.number().optional().describe("Canvas Y position (default 150)"),
		},
		async ({ space, token, task_id, needs, canvas_x, canvas_y }) => {
			const access = await resolveAccess(token, space, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const wDoc = syncServer.getDoc<WeavingDoc>(weavingDocId(space));
			if (!wDoc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No weaving doc found" }) }], isError: true };
			}

			// Verify task exists in rTasks board
			const boardSlug = wDoc.boardSlug || space;
			const board = syncServer.getDoc<BoardDoc>(boardDocId(space, boardSlug));
			if (!board?.tasks[task_id]) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Task not found in rTasks board" }) }], isError: true };
			}

			syncServer.changeDoc<WeavingDoc>(weavingDocId(space), `Place task ${task_id} on canvas`, (d) => {
				if (!d.weavingOverlays) (d as any).weavingOverlays = {};
				d.weavingOverlays[task_id] = {
					rtasksId: task_id,
					needs,
					canvasX: canvas_x ?? 400,
					canvasY: canvas_y ?? 150,
					notes: '',
					links: [],
				};
			});

			return { content: [{ type: "text", text: JSON.stringify({ id: task_id, placed: true, needs }) }] };
		},
	);

	server.tool(
		"rtime_list_time_logs",
		"List external time logs (imported from backlog-md)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().optional().describe("JWT auth token"),
			status: z.string().optional().describe("Filter by status (pending, commitment_created, settled)"),
			limit: z.number().optional().describe("Max results (default 50)"),
		},
		async ({ space, token, status, limit }) => {
			const access = await resolveAccess(token, space, false);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<ExternalTimeLogsDoc>(externalTimeLogsDocId(space));
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No time logs found" }) }] };
			}

			let logs = Object.values(doc.logs || {});
			if (status) logs = logs.filter(l => l.status === status);

			logs.sort((a, b) => b.loggedAt - a.loggedAt);
			logs = logs.slice(0, limit || 50);

			const summary = logs.map(l => ({
				id: l.id,
				backlogTaskTitle: l.backlogTaskTitle,
				memberName: l.memberName,
				hours: l.hours,
				skill: l.skill,
				status: l.status,
				loggedAt: l.loggedAt,
			}));

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

	server.tool(
		"rtime_create_commitment",
		"Create a new time commitment (requires auth token + space membership)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			member_name: z.string().describe("Name of the committing member"),
			hours: z.number().min(1).max(10).describe("Hours committed (1-10)"),
			skill: z.enum(["facilitation", "design", "tech", "outreach", "logistics"]).describe("Skill type"),
			desc: z.string().describe("Description of the commitment"),
		},
		async ({ space, token, member_name, hours, skill, desc }) => {
			const access = await resolveAccess(token, space, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const docId = commitmentsDocId(space);
			const doc = syncServer.getDoc<CommitmentsDoc>(docId);
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No commitments data found" }) }], isError: true };
			}

			const commitmentId = `cmt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
			const now = Date.now();

			syncServer.changeDoc<CommitmentsDoc>(docId, `Create commitment by ${member_name}`, (d) => {
				if (!d.items) (d as any).items = {};
				d.items[commitmentId] = {
					id: commitmentId,
					memberName: member_name,
					hours,
					skill: skill as Skill,
					desc,
					createdAt: now,
					status: "active",
					ownerDid: (access.claims?.did as string) ?? undefined,
				};
			});

			return { content: [{ type: "text", text: JSON.stringify({ id: commitmentId, created: true }) }] };
		},
	);
}
