import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
initStderrTee("scheduling");
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { scheduleEvent } from "./tools/schedule-event.js";
import { scheduleList } from "./tools/schedule-list.js";
import { scheduleGet } from "./tools/schedule-get.js";
import { scheduleUpdate } from "./tools/schedule-update.js";
import { scheduleCancel } from "./tools/schedule-cancel.js";
import { closeDriver, getUserTimezone } from "./lib/neo4j.js";
import { formatCombined } from "./lib/time-format.js";
// Cast to `any` to avoid TS2589. TypeScript hits its 50-level instantiation
// limit when accumulating typed tool signatures on a generic McpServer.
// Individual tool handlers retain full type safety in their own files.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const server = new McpServer({
    name: "maxy-scheduling",
    version: "0.1.0",
});
const accountId = process.env.ACCOUNT_ID;
if (!accountId) {
    throw new Error("ACCOUNT_ID environment variable is required");
}
// ---------------------------------------------------------------------------
// Shared schema fragments
// ---------------------------------------------------------------------------
const eventStatusSchema = z
    .enum(["scheduled", "due", "completed", "cancelled"])
    .optional()
    .describe("Event status");
// ---------------------------------------------------------------------------
// schedule-event
// ---------------------------------------------------------------------------
server.tool("schedule-event", "Create a scheduled event or appointment. For one-time events, provide startDate. For recurring events, also provide a recurrence (cron expression, e.g. '0 8 * * 1-5' for weekdays at 8am). Pass sessionKey to link to the current conversation. Pass action to auto-execute an MCP tool when the event fires.", {
    name: z.string().describe("Short event title"),
    description: z.string().optional().describe("Event details"),
    startDate: z.string().describe("ISO 8601 datetime for the event start"),
    endDate: z.string().optional().describe("ISO 8601 datetime for the event end"),
    location: z.string().optional().describe("Event location (free text)"),
    recurrence: z.string().optional().describe("Cron expression for recurring events (5-field: min hour dom mon dow). Omit for one-time events."),
    timezone: z.string().optional().describe("IANA timezone (e.g. Europe/London). Defaults to account timezone."),
    sessionKey: z.string().optional().describe("Current session key — links this event to the conversation"),
    action: z.object({
        plugin: z.string().describe("Plugin name that exposes the tool (e.g. 'waitlist')"),
        tool: z.string().describe("MCP tool name to call when the event fires (e.g. 'waitlist-extract')"),
        args: z.record(z.unknown()).optional().describe("Arguments to pass to the tool call"),
    }).optional().describe("Automated action: dispatches an MCP tool call when the event fires. The plugin must exist in platform/plugins/."),
}, async (params) => {
    try {
        const timezone = await getUserTimezone(accountId);
        console.error(`[schedule-event] formatted for timezone: ${timezone}`);
        const result = await scheduleEvent({ ...params, accountId });
        const parts = [`Event created: "${params.name}" (ID: ${result.eventId})`];
        parts.push(`starts: ${formatCombined(params.startDate, timezone)}`);
        if (params.recurrence)
            parts.push(`recurs: ${params.recurrence}`);
        if (result.nextRun)
            parts.push(`next run: ${formatCombined(result.nextRun, timezone)}`);
        if (params.action)
            parts.push(`action: ${params.action.plugin}/${params.action.tool}`);
        return { content: [{ type: "text", text: parts.join(", ") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed to create event: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// schedule-list
// ---------------------------------------------------------------------------
server.tool("schedule-list", "List scheduled events. Filter by status, date range, or upcoming only. Ordered by next occurrence.", {
    status: eventStatusSchema,
    from: z.string().optional().describe("ISO 8601 date — events starting on or after this date"),
    to: z.string().optional().describe("ISO 8601 date — events starting on or before this date"),
    upcoming: z.boolean().optional().describe("If true, show only scheduled/due events with future dates"),
    limit: z.number().optional().describe("Max results (default 50)"),
}, async (params) => {
    try {
        const timezone = await getUserTimezone(accountId);
        console.error(`[schedule-list] formatted for timezone: ${timezone}`);
        const events = await scheduleList({ ...params, accountId });
        if (events.length === 0) {
            return { content: [{ type: "text", text: "No events match the filters." }] };
        }
        const lines = events.map((e) => {
            const recur = e.recurrence ? ` [recurring: ${e.recurrence}]` : "";
            const loc = e.location ? ` @ ${e.location}` : "";
            const next = e.nextRun ? ` next: ${formatCombined(e.nextRun, timezone)}` : "";
            return `${e.eventStatus.toUpperCase()} — ${e.name} (${formatCombined(e.startDate, timezone)})${loc}${recur}${next} (${e.eventId})`;
        });
        return { content: [{ type: "text", text: `${events.length} event(s):\n\n${lines.join("\n")}` }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `List failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// schedule-get
// ---------------------------------------------------------------------------
server.tool("schedule-get", "Fetch a single event with its full details, notes history, and all graph relationships.", {
    eventId: z.string().describe("Event ID (UUID)"),
}, async ({ eventId }) => {
    try {
        const timezone = await getUserTimezone(accountId);
        console.error(`[schedule-get] formatted for timezone: ${timezone}`);
        const event = await scheduleGet(eventId, accountId);
        if (!event) {
            return { content: [{ type: "text", text: `Event not found: ${eventId}` }] };
        }
        const relLines = event.relationships.length > 0
            ? event.relationships
                .map((r) => `  -[${r.type}]-> ${r.targetLabel}: ${r.targetName ?? r.targetId}`)
                .join("\n")
            : "  (none)";
        const parts = [
            `Event: ${event.name}`,
            `ID: ${event.eventId}`,
            `Status: ${event.eventStatus}`,
            event.description ? `Description: ${event.description}` : null,
            `Starts: ${formatCombined(event.startDate, timezone)}`,
            event.endDate ? `Ends: ${formatCombined(event.endDate, timezone)}` : null,
            event.location ? `Location: ${event.location}` : null,
            event.recurrence ? `Recurrence: ${event.recurrence}` : null,
            event.timezone ? `Timezone: ${event.timezone}` : null,
            event.nextRun ? `Next run: ${formatCombined(event.nextRun, timezone)}` : null,
            event.lastTriggered ? `Last triggered: ${formatCombined(event.lastTriggered, timezone)}` : null,
            event.skipNext ? `Skip next: yes` : null,
            event.notes ? `\nNotes:\n${event.notes}` : null,
            ``,
            `Relationships:\n${relLines}`,
            ``,
            `Created: ${formatCombined(event.createdAt, timezone)} | Updated: ${formatCombined(event.updatedAt, timezone)}`,
        ].filter((line) => line !== null);
        return { content: [{ type: "text", text: parts.join("\n") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Get failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// schedule-update
// ---------------------------------------------------------------------------
server.tool("schedule-update", "Update an event's properties or append a note. Use skipNext=true on recurring events to skip the next occurrence without cancelling the series. Pass action to set/replace an automated action, or action=null to clear it.", {
    eventId: z.string().describe("Event ID (UUID)"),
    name: z.string().optional().describe("New event title"),
    description: z.string().optional().describe("New description"),
    startDate: z.string().optional().describe("New start date (ISO 8601)"),
    endDate: z.string().optional().describe("New end date (ISO 8601)"),
    location: z.string().optional().describe("New location"),
    eventStatus: eventStatusSchema,
    recurrence: z.string().optional().describe("New cron expression"),
    timezone: z.string().optional().describe("New IANA timezone"),
    skipNext: z.boolean().optional().describe("Skip the next occurrence of a recurring event"),
    note: z.string().optional().describe("Note appended to event history with timestamp"),
    action: z.object({
        plugin: z.string().describe("Plugin name that exposes the tool"),
        tool: z.string().describe("MCP tool name to call when the event fires"),
        args: z.record(z.unknown()).optional().describe("Arguments to pass to the tool call"),
    }).nullable().optional().describe("Set/replace the automated action, or pass null to clear it"),
}, async (params) => {
    try {
        await scheduleUpdate({ ...params, accountId });
        if (params.skipNext) {
            return { content: [{ type: "text", text: `Skipped next occurrence of event: ${params.eventId}` }] };
        }
        return { content: [{ type: "text", text: `Event updated: ${params.eventId}` }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Update failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// schedule-cancel
// ---------------------------------------------------------------------------
server.tool("schedule-cancel", "Cancel a scheduled event. For recurring events, this cancels the entire series. Idempotent — cancelling an already-cancelled event is a no-op.", {
    eventId: z.string().describe("Event ID (UUID)"),
    note: z.string().optional().describe("Cancellation reason"),
}, async (params) => {
    try {
        await scheduleCancel({ ...params, accountId });
        return { content: [{ type: "text", text: `Event cancelled: ${params.eventId}` }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Cancel failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// time-resolve
// ---------------------------------------------------------------------------
server.tool("time-resolve", "Convert a UTC timestamp to the user's local timezone with a human-readable relative delta. Use when the agent needs to present a time to the user or reason about temporal proximity.", {
    expression: z.string().describe("ISO 8601 datetime string to convert (e.g. '2026-04-03T11:00:00.000Z')"),
}, async ({ expression }) => {
    try {
        const timezone = await getUserTimezone(accountId);
        const result = formatCombined(expression, timezone);
        console.error(`[time-resolve] ${expression} → ${result} (${timezone})`);
        return { content: [{ type: "text", text: result }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Time resolve failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// Shutdown + start
// ---------------------------------------------------------------------------
process.on("SIGINT", async () => {
    await closeDriver();
    process.exit(0);
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map