import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
  Root,
  RootsListChangedNotificationSchema,
} from "@modelcontextprotocol/sdk/types.js";

// Track roots by session id
export const roots: Map<string | undefined, Root[]> = new Map<
  string | undefined,
  Root[]
>();

/**
 * Get the latest the client roots list for the session.
 *
 * - Request and cache the roots list for the session if it has not been fetched before.
 * - Return the cached roots list for the session if it exists.
 *
 * When requesting the roots list for a session, it also sets up a `roots/list_changed`
 * notification handler. This ensures that updates are automatically fetched and handled
 * in real-time.
 *
 * This function is idempotent. It should only request roots from the client once per session,
 * returning the cached version thereafter.
 *
 * @param {McpServer} server - An instance of the MCP server used to communicate with the client.
 * @param {string} [sessionId] - An optional session id used to associate the roots list with a specific client session.
 *
 * @throws {Error} In case of a failure to request the roots from the client, an error log message is sent.
 */
export const syncRoots = async (server: McpServer, sessionId?: string) => {
  const clientCapabilities = server.server.getClientCapabilities() || {};
  const clientSupportsRoots: boolean = clientCapabilities?.roots !== undefined;

  // Fetch the roots list for this client
  if (clientSupportsRoots) {
    // Function to request the updated roots list from the client
    const requestRoots = async () => {
      try {
        // Request the updated roots list from the client
        const response = await server.server.listRoots();
        if (response && "roots" in response) {
          // Store the roots list for this client
          roots.set(sessionId, response.roots);

          // Notify the client of roots received
          await server.sendLoggingMessage(
            {
              level: "info",
              logger: "everything-server",
              data: `Roots updated: ${response?.roots?.length} root(s) received from client`,
            },
            sessionId
          );
        } else {
          await server.sendLoggingMessage(
            {
              level: "info",
              logger: "everything-server",
              data: "Client returned no roots set",
            },
            sessionId
          );
        }
      } catch (error) {
        await server.sendLoggingMessage(
          {
            level: "error",
            logger: "everything-server",
            data: `Failed to request roots from client: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
          sessionId
        );
      }
    };

    // If the roots have not been synced for this client,
    // set notification handler and request initial roots
    if (!roots.has(sessionId)) {
      // Set the list changed notification handler
      server.server.setNotificationHandler(
        RootsListChangedNotificationSchema,
        requestRoots
      );

      // Request the initial roots list immediately
      await requestRoots();
    }

    // Return the roots list for this client
    return roots.get(sessionId);
  }
};
