import React, { useMemo } from 'react';
import { LiveChannelScope, isLiveChannelMessageEvent } from '@grafana/data';
import { getGrafanaLiveSrv, getBackendSrv, logDebug, config } from '@grafana/runtime';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
export { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
export { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
import { filter } from 'rxjs';
import { v4 } from 'uuid';
import { LLM_PLUGIN_ID, LLM_PLUGIN_ROUTE } from './constants.mjs';

const MCP_GRAFANA_PATH = "mcp/grafana";
class GrafanaLiveTransport {
  constructor(path) {
    this._grafanaLiveSrv = getGrafanaLiveSrv();
    if (path === void 0) {
      const pathId = v4();
      path = `${MCP_GRAFANA_PATH}/${pathId}`;
    }
    this._subscribeChannel = {
      scope: LiveChannelScope.Plugin,
      namespace: LLM_PLUGIN_ID,
      path: `${path}/subscribe`
    };
    this._publishChannel = {
      scope: LiveChannelScope.Plugin,
      namespace: LLM_PLUGIN_ID,
      path: `${path}/publish`
    };
  }
  async start() {
    if (this._stream !== void 0) {
      throw new Error(
        "GrafanaLiveTransport already started! If using Client class, note that connect() calls start() automatically."
      );
    }
    const stream = this._grafanaLiveSrv.getStream(this._subscribeChannel).pipe(filter((event) => isLiveChannelMessageEvent(event)));
    this._stream = stream;
    stream.subscribe((event) => {
      let message;
      try {
        message = JSONRPCMessageSchema.parse(event.message);
      } catch (error) {
        this.onerror?.(error);
        return;
      }
      this.onmessage?.(message);
    });
  }
  async send(message) {
    if (this._stream === void 0) {
      throw new Error("not connected");
    }
    const hasPublishOptions = this._grafanaLiveSrv.publish?.length >= 3;
    if (hasPublishOptions) {
      const options = { useSocket: true };
      this._grafanaLiveSrv.publish(this._publishChannel, message, options);
    }
    const centrifugeSubscription = (
      // @ts-expect-error
      this._grafanaLiveSrv.deps?.centrifugeSrv?.getChannel?.(
        this._publishChannel
      )?.subscription
    );
    if (centrifugeSubscription) {
      return centrifugeSubscription.publish(message);
    }
    console.warn(
      "Websocket subscription not available, falling back to HTTP publish. This may fail in HA setups. If you see this, please create an issue at https://github.com/grafana/grafana-llm-app/issues/new."
    );
    await this._grafanaLiveSrv.publish(this._publishChannel, message);
  }
  async close() {
    this._stream = void 0;
  }
}
const clientMap = /* @__PURE__ */ new Map();
const MCPClientContext = React.createContext(null);
function clientKey(appName, appVersion) {
  return `${appName}-${appVersion}`;
}
async function enabled$1() {
  try {
    const settings = await getBackendSrv().get(
      `${LLM_PLUGIN_ROUTE}/settings`,
      void 0,
      void 0,
      {
        showSuccessAlert: false,
        showErrorAlert: false
      }
    );
    if (!settings.enabled) {
      return false;
    }
    if (settings.jsonData.mcp?.enabled !== void 0) {
      return !!settings.jsonData.mcp?.enabled;
    }
    return !settings.jsonData.mcp?.disabled;
  } catch (e) {
    logDebug(String(e));
    logDebug(
      "Failed to check if LLM provider is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored."
    );
    return false;
  }
}
function streamableHTTPURL(appId = LLM_PLUGIN_ID, mcpPath = MCP_GRAFANA_PATH) {
  let grafanaUrl = config.appUrl || "http://localhost:3000/";
  if (!grafanaUrl.endsWith("/")) {
    grafanaUrl = `${grafanaUrl}/`;
  }
  if (!mcpPath.startsWith("/")) {
    mcpPath = `/${mcpPath}`;
  }
  return new URL(`${grafanaUrl}api/plugins/${appId}/resources${mcpPath}`);
}
function createClientResource({
  appName,
  appVersion,
  mcpAppName,
  mcpAppPath
}) {
  let status = "pending";
  let result = null;
  let error = null;
  const key = clientKey(appName, appVersion);
  const promise = (async () => {
    if (clientMap.has(key)) {
      result = clientMap.get(key);
      if (result.error) {
        status = "error";
        error = result.error;
        throw result.error;
      }
      status = "success";
      return result;
    }
    try {
      const isEnabled = await enabled$1();
      if (!isEnabled) {
        status = "success";
        result = { client: null, enabled: isEnabled };
        clientMap.set(key, result);
        return result;
      }
      const client = new Client({
        name: appName,
        version: appVersion
      });
      const transport = new StreamableHTTPClientTransport(
        streamableHTTPURL(mcpAppName, mcpAppPath),
        {
          reconnectionOptions: {
            maxRetries: 5,
            initialReconnectionDelay: 1e3,
            maxReconnectionDelay: 5e3,
            reconnectionDelayGrowFactor: 1.5
          }
        }
      );
      await client.connect(transport);
      result = { client, enabled: isEnabled };
      clientMap.set(key, result);
      status = "success";
      return result;
    } catch (e) {
      status = "error";
      error = e;
      result = { client: null, enabled: false, error };
      clientMap.set(key, result);
      throw e;
    }
  })();
  return {
    read() {
      if (status === "pending") {
        throw promise;
      } else if (status === "error") {
        throw error;
      } else if (status === "success" && result) {
        return result;
      }
      throw new Error("Unexpected resource state");
    }
  };
}
function MCPClientProvider({
  appName,
  appVersion,
  mcpAppName = LLM_PLUGIN_ID,
  mcpAppPath = MCP_GRAFANA_PATH,
  children
}) {
  const resource = useMemo(
    () => createClientResource({
      appName,
      appVersion,
      mcpAppName,
      mcpAppPath
    }),
    [appName, appVersion, mcpAppName, mcpAppPath]
  );
  const result = resource.read();
  React.useEffect(() => {
    return () => {
      if (result?.client) {
        result.client.close();
      }
      clientMap.delete(clientKey(appName, appVersion));
    };
  }, [result, appName, appVersion]);
  return /* @__PURE__ */ React.createElement(MCPClientContext.Provider, { value: result }, children);
}
function useMCPClient() {
  const client = React.useContext(MCPClientContext);
  if (client === null) {
    throw new Error("MCP is not enabled in this Grafana instance.");
  }
  return client;
}
function convertToolsToOpenAI(tools) {
  return tools.map(convertToolToOpenAI);
}
function convertToolToOpenAI(tool) {
  return {
    type: "function",
    function: {
      name: tool.name,
      description: tool.description,
      parameters: tool.inputSchema.properties !== void 0 ? tool.inputSchema : void 0
    }
  };
}

export { GrafanaLiveTransport, MCPClientProvider, convertToolsToOpenAI, enabled$1 as enabled, streamableHTTPURL, useMCPClient };
//# sourceMappingURL=mcp.mjs.map
