import { ElicitationRequestToast } from "@/client/components/elicitation/ElicitationRequestToast";
import { InspectorDashboard } from "@/client/components/InspectorDashboard";
import { Layout } from "@/client/components/Layout";
import { OAuthCallback } from "@/client/components/OAuthCallback";
import { SamplingRequestToast } from "@/client/components/sampling/SamplingRequestToast";
import { Toaster } from "@/client/components/ui/sonner";
import {
  LocalStorageProvider,
  McpClientProvider,
  type McpServer,
} from "mcp-use/react";
import { useEffect, useMemo, useRef } from "react";
import { Route, BrowserRouter as Router, Routes } from "react-router";
import { toast } from "sonner";
import { InspectorProvider, useInspector } from "./context/InspectorContext";
import { ThemeProvider } from "./context/ThemeContext";
import { WidgetDebugProvider } from "./context/WidgetDebugContext";

/**
 * Syncs the active tab from InspectorContext into a ref readable by
 * McpClientProvider callbacks defined outside the InspectorProvider tree.
 */
function InspectorTabSync({
  activeTabRef,
}: {
  activeTabRef: React.MutableRefObject<string>;
}) {
  const { activeTab } = useInspector();
  useEffect(() => {
    activeTabRef.current = activeTab;
  }, [activeTab, activeTabRef]);
  return null;
}

/**
 * Root React component that configures application providers, routing, and toast-based handlers for sampling and elicitation requests in the inspector UI.
 *
 * Creates a LocalStorageProvider for saved connections when not running in embedded mode (determined via the `embedded=true` URL parameter), initializes the MCP client with RPC logging and lifecycle callbacks, and renders the inspector routes (including the OAuth callback and main dashboard) inside theme and inspector contexts. Sampling and elicitation requests are surfaced as persistent toasts that allow viewing details, approving/denying, or opening supplied URLs.
 *
 * @returns The app's React element tree.
 */
function App() {
  const activeTabRef = useRef<string>("tools");

  // Check if embedded mode is active from URL params
  const urlParams = new URLSearchParams(window.location.search);
  const isEmbedded = urlParams.get("embedded") === "true";

  // Check if theme is forced via URL params
  const forcedTheme = urlParams.get("theme") as
    | "light"
    | "dark"
    | "system"
    | null;

  // Create storage provider (only in non-embedded mode)
  const storageProvider = useMemo(
    () =>
      isEmbedded
        ? undefined
        : new LocalStorageProvider("mcp-inspector-connections"),
    [isEmbedded]
  );

  // Read the proxy path injected by the inspector server (undefined when served
  // from a Python/custom server that fetches static HTML and has no proxy).
  const injectedProxyPath = (window as Window & { __MCP_PROXY_URL__?: string })
    .__MCP_PROXY_URL__;
  const proxyAddress = injectedProxyPath
    ? `${window.location.origin}${injectedProxyPath}`
    : undefined;

  // App-level so it fires regardless of route, and after <Toaster /> mounts.
  useEffect(() => {
    const authError = urlParams.get("auth_error");
    if (!authError) return;
    const description = urlParams.get("auth_error_description");
    toast.error(`OAuth authentication failed: ${description || authError}`, {
      duration: Infinity,
      closeButton: true,
    });
    // Clone before mutating so we don't disturb the params consumed above.
    const cleaned = new URLSearchParams(urlParams);
    cleaned.delete("auth_error");
    cleaned.delete("auth_error_description");
    const search = cleaned.toString();
    window.history.replaceState(
      {},
      "",
      `${window.location.pathname}${search ? `?${search}` : ""}`
    );
  }, []);

  return (
    <ThemeProvider forcedTheme={forcedTheme || undefined}>
      <WidgetDebugProvider>
        <McpClientProvider
          storageProvider={storageProvider}
          enableRpcLogging={true}
          defaultCallbackUrl={`${window.location.origin}/inspector/oauth/callback`}
          defaultAutoProxyFallback={
            proxyAddress ? { enabled: true, proxyAddress } : false
          }
          clientInfo={{
            name: "mcp-use Inspector",
            version: (window as any).__INSPECTOR_VERSION__,
            websiteUrl: "https://mcp-use.com",
            icons: [{ src: "https://mcp-use.com/logo.png" }],
            capabilities: {
              extensions: {
                "io.modelcontextprotocol/ui": {
                  mimeTypes: ["text/html;profile=mcp-app"],
                },
              },
            },
          }}
          onServerAdded={(id: string, server: McpServer) => {
            console.log("[Inspector] Server added:", id, server.state);
          }}
          onServerRemoved={(id: string) => {
            console.log("[Inspector] Server removed:", id);
          }}
          onServerStateChange={(id: string, state: McpServer["state"]) => {
            console.log("[Inspector] Server state changed:", id, state);
          }}
          onSamplingRequest={(
            request,
            _serverId,
            serverName,
            approve,
            reject
          ) => {
            const toastId = toast(
              <SamplingRequestToast
                requestId={request.id}
                serverName={serverName}
                onViewDetails={() => {
                  const event = new CustomEvent("navigate-to-sampling", {
                    detail: { requestId: request.id },
                  });
                  window.dispatchEvent(event);
                  toast.dismiss(toastId);
                }}
                onApprove={(defaultResponse) => {
                  approve(request.id, defaultResponse);
                  toast.success("Sampling request approved");
                  toast.dismiss(toastId);
                }}
                onDeny={() => {
                  reject(request.id, "User denied from toast");
                  toast.dismiss(toastId);
                }}
              />,
              { duration: Infinity }
            );
          }}
          onElicitationRequest={(
            request,
            _serverId,
            serverName,
            _approve,
            reject
          ) => {
            // When the chat tab is active, elicitation is rendered inline — no toast needed.
            if (activeTabRef.current === "chat") {
              return;
            }

            const mode = request.request.mode || "form";
            const message = request.request.message;
            const url =
              mode === "url" && "url" in request.request
                ? request.request.url
                : undefined;

            const toastId = toast(
              <ElicitationRequestToast
                requestId={request.id}
                serverName={serverName}
                mode={mode}
                message={message}
                url={url}
                onViewDetails={() => {
                  const event = new CustomEvent("navigate-to-elicitation", {
                    detail: { requestId: request.id },
                  });
                  window.dispatchEvent(event);
                  toast.dismiss(toastId);
                }}
                onOpenUrl={
                  mode === "url" && url
                    ? () => {
                        window.open(url, "_blank");
                        toast.dismiss(toastId);
                      }
                    : undefined
                }
                onCancel={() => {
                  reject(request.id, "User cancelled from toast");
                  toast.dismiss(toastId);
                }}
              />,
              { duration: Infinity }
            );
          }}
        >
          <InspectorProvider>
            <InspectorTabSync activeTabRef={activeTabRef} />
            <Router basename="/inspector">
              <Routes>
                <Route path="/oauth/callback" element={<OAuthCallback />} />
                <Route
                  path="/"
                  element={
                    <Layout>
                      <InspectorDashboard />
                    </Layout>
                  }
                />
              </Routes>
            </Router>
            <Toaster position="top-center" />
          </InspectorProvider>
        </McpClientProvider>
      </WidgetDebugProvider>
    </ThemeProvider>
  );
}

export default App;
