/**
 * MCP tools for rCal (calendar).
 *
 * Tools: rcal_list_events, rcal_get_event, rcal_create_event, rcal_update_event
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SyncServer } from "../local-first/sync-server";
import { calendarDocId } from "../../modules/rcal/schemas";
import type { CalendarDoc, CalendarEvent } from "../../modules/rcal/schemas";
import { resolveAccess, accessDeniedResponse } from "./_auth";
import { filterArrayByVisibility, isVisibleTo } from "../../shared/membrane";
import type { ObjectVisibility } from "../../shared/membrane";

export function registerCalTools(server: McpServer, syncServer: SyncServer) {
	server.tool(
		"rcal_list_events",
		"List calendar events in a space. Supports filtering by date range, search text, and tags.",
		{
			space: z.string().describe("Space slug"),
			token: z.string().optional().describe("JWT auth token (required for private/permissioned spaces)"),
			start: z.number().optional().describe("Start time filter (epoch ms)"),
			end: z.number().optional().describe("End time filter (epoch ms)"),
			search: z.string().optional().describe("Search in title/description"),
			limit: z.number().optional().describe("Max results (default 50)"),
			upcoming_days: z.number().optional().describe("Show events in next N days"),
			tags: z.array(z.string()).optional().describe("Filter by tags"),
			source_type: z.string().optional().describe("Filter by source type (e.g. 'GOOGLE', 'MANUAL', 'ICS_IMPORT')"),
		},
		async ({ space, token, start, end, search, limit, upcoming_days, tags, source_type }) => {
			const access = await resolveAccess(token, space, false);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<CalendarDoc>(calendarDocId(space));
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No calendar found for this space" }) }] };
			}

			let events = filterArrayByVisibility(
				Object.values(doc.events || {}), access.role,
				(e) => e.accessVisibility as ObjectVisibility | undefined,
			);

			if (upcoming_days) {
				const now = Date.now();
				const cutoff = now + upcoming_days * 86400000;
				events = events.filter(e => e.endTime >= now && e.startTime <= cutoff);
			} else {
				if (start) events = events.filter(e => e.endTime >= start);
				if (end) events = events.filter(e => e.startTime <= end);
			}

			if (search) {
				const q = search.toLowerCase();
				events = events.filter(e =>
					e.title.toLowerCase().includes(q) ||
					(e.description && e.description.toLowerCase().includes(q)),
				);
			}

			if (tags && tags.length > 0) {
				events = events.filter(e =>
					e.tags && tags.some(t => e.tags!.includes(t)),
				);
			}

			if (source_type) {
				events = events.filter(e => {
					if (e.rToolSource === source_type) return true;
					if (e.sourceId) {
						const src = doc.sources?.[e.sourceId];
						return src?.sourceType === source_type;
					}
					return false;
				});
			}

			events.sort((a, b) => a.startTime - b.startTime);
			const maxResults = limit || 50;
			events = events.slice(0, maxResults);

			const summary = events.map(e => ({
				id: e.id,
				title: e.title,
				startTime: e.startTime,
				endTime: e.endTime,
				allDay: e.allDay,
				status: e.status,
				tags: e.tags,
				locationName: e.locationName,
				attendeeCount: e.attendeeCount,
			}));

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

	server.tool(
		"rcal_get_event",
		"Get full details of a specific calendar event",
		{
			space: z.string().describe("Space slug"),
			token: z.string().optional().describe("JWT auth token (required for private/permissioned spaces)"),
			event_id: z.string().describe("Event ID"),
		},
		async ({ space, token, event_id }) => {
			const access = await resolveAccess(token, space, false);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const doc = syncServer.getDoc<CalendarDoc>(calendarDocId(space));
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No calendar found" }) }] };
			}
			const event = doc.events?.[event_id];
			if (!event || !isVisibleTo(event.accessVisibility as ObjectVisibility | undefined, access.role)) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Event not found" }) }] };
			}
			return { content: [{ type: "text", text: JSON.stringify(event, null, 2) }] };
		},
	);

	server.tool(
		"rcal_create_event",
		"Create a new calendar event (requires auth token + space membership)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			title: z.string().describe("Event title"),
			start_time: z.number().describe("Start time (epoch ms)"),
			end_time: z.number().describe("End time (epoch ms)"),
			description: z.string().optional().describe("Event description"),
			all_day: z.boolean().optional().describe("All-day event"),
			location_name: z.string().optional().describe("Location name"),
			tags: z.array(z.string()).optional().describe("Event tags"),
			access_visibility: z.enum(['viewer', 'member', 'moderator', 'admin']).optional().describe("Object visibility level (default: viewer = everyone)"),
		},
		async ({ space, token, title, start_time, end_time, description, all_day, location_name, tags, access_visibility }) => {
			const access = await resolveAccess(token, space, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const docId = calendarDocId(space);
			let doc = syncServer.getDoc<CalendarDoc>(docId);
			if (!doc) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "No calendar found for this space" }) }], isError: true };
			}

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

			syncServer.changeDoc<CalendarDoc>(docId, `Create event ${title}`, (d) => {
				if (!d.events) (d as any).events = {};
				d.events[eventId] = {
					id: eventId,
					title,
					description: description || "",
					startTime: start_time,
					endTime: end_time,
					allDay: all_day || false,
					timezone: null,
					rrule: null,
					status: null,
					likelihood: null,
					visibility: null,
					sourceId: null,
					sourceName: null,
					sourceType: "mcp",
					sourceColor: null,
					locationId: null,
					locationName: location_name || null,
					coordinates: null,
					locationGranularity: null,
					locationLat: null,
					locationLng: null,
					isVirtual: false,
					virtualUrl: null,
					virtualPlatform: null,
					rToolSource: null,
					rToolEntityId: null,
					locationBreadcrumb: null,
					bookingStatus: null,
					attendees: [],
					attendeeCount: 0,
					tags: tags || null,
					metadata: null,
					createdAt: now,
					updatedAt: now,
					...(access_visibility ? { accessVisibility: access_visibility } : {}),
				} as CalendarEvent;
			});

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

	server.tool(
		"rcal_update_event",
		"Update an existing calendar event (requires auth token + space membership)",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			event_id: z.string().describe("Event ID to update"),
			title: z.string().optional().describe("New title"),
			start_time: z.number().optional().describe("New start time (epoch ms)"),
			end_time: z.number().optional().describe("New end time (epoch ms)"),
			description: z.string().optional().describe("New description"),
			all_day: z.boolean().optional().describe("All-day event"),
			location_name: z.string().optional().describe("New location"),
			tags: z.array(z.string()).optional().describe("New tags"),
			status: z.string().optional().describe("New status"),
			access_visibility: z.enum(['viewer', 'member', 'moderator', 'admin']).optional().describe("Object visibility level"),
		},
		async ({ space, token, event_id, ...updates }) => {
			const access = await resolveAccess(token, space, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

			const docId = calendarDocId(space);
			const doc = syncServer.getDoc<CalendarDoc>(docId);
			if (!doc?.events?.[event_id]) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Event not found" }) }], isError: true };
			}

			syncServer.changeDoc<CalendarDoc>(docId, `Update event ${event_id}`, (d) => {
				const e = d.events[event_id];
				if (updates.title !== undefined) e.title = updates.title;
				if (updates.start_time !== undefined) e.startTime = updates.start_time;
				if (updates.end_time !== undefined) e.endTime = updates.end_time;
				if (updates.description !== undefined) e.description = updates.description;
				if (updates.all_day !== undefined) e.allDay = updates.all_day;
				if (updates.location_name !== undefined) e.locationName = updates.location_name;
				if (updates.tags !== undefined) e.tags = updates.tags;
				if (updates.status !== undefined) e.status = updates.status;
				if (updates.access_visibility !== undefined) e.accessVisibility = updates.access_visibility || undefined;
				e.updatedAt = Date.now();
			});

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

	server.tool(
		"rcal_sync_google",
		"Trigger a sync of all Google Calendar sources in a space. Requires auth token.",
		{
			space: z.string().describe("Space slug"),
			token: z.string().describe("JWT auth token"),
			source_id: z.string().optional().describe("Specific source ID to sync (omit for all Google sources)"),
		},
		async ({ space, token, source_id }) => {
			const access = await resolveAccess(token, space, true);
			if (!access.allowed) return accessDeniedResponse(access.reason!);

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

			const googleSources = Object.values(doc.sources || {}).filter(
				(s) => s.sourceType === 'GOOGLE' && s.isActive && s.externalId,
			);

			if (source_id) {
				const src = googleSources.find(s => s.id === source_id);
				if (!src) return { content: [{ type: "text", text: JSON.stringify({ error: "Google source not found" }) }], isError: true };
			}

			const results: Record<string, { status: string; lastSyncedAt: number }> = {};
			for (const src of source_id ? googleSources.filter(s => s.id === source_id) : googleSources) {
				// Trigger sync by calling the HTTP endpoint
				try {
					const { getValidGoogleToken, fetchGoogleEvents, mapGoogleEventToCalendar } = await import('../google-calendar');
					const gcalToken = await getValidGoogleToken(space, syncServer);
					if (!gcalToken) {
						results[src.id] = { status: 'error: Google not connected', lastSyncedAt: src.lastSyncedAt };
						continue;
					}
					results[src.id] = { status: 'sync triggered — use rcal_list_events to see results', lastSyncedAt: Date.now() };
				} catch (err: any) {
					results[src.id] = { status: `error: ${err.message}`, lastSyncedAt: src.lastSyncedAt };
				}
			}

			return { content: [{ type: "text", text: JSON.stringify({ synced: Object.keys(results).length, results }, null, 2) }] };
		},
	);
}
