import React, {
  StrictMode,
  useCallback,
  useEffect,
  useRef,
  useState,
} from "react";
import { ErrorBoundary } from "./ErrorBoundary.js";
import { getMcpAppsBridge } from "./mcp-apps-bridge.js";
import { ThemeProvider } from "./ThemeProvider.js";
import { WidgetControls } from "./WidgetControls.js";

// Constants for height management
const HEIGHT_DEBOUNCE_MS = 150; // Debounce duration to wait for animations to settle
const MIN_HEIGHT_CHANGE_PX = 5; // Minimum height change to trigger notification

interface McpUseProviderProps {
  children: React.ReactNode;
  /**
   * Enable debug button in WidgetControls component
   * @default false
   */
  debugger?: boolean;
  /**
   * Enable view controls (fullscreen/pip) in WidgetControls component
   * - `true` = show both pip and fullscreen buttons
   * - `"pip"` = show only pip button
   * - `"fullscreen"` = show only fullscreen button
   * @default false
   */
  viewControls?: boolean | "pip" | "fullscreen";
  /**
   * Automatically notify host about container height changes for auto-sizing
   * Uses MCP Apps `ui/notifications/size-changed`.
   * Uses ResizeObserver to monitor the children container
   * @default false
   */
  autoSize?: boolean;
  /**
   * Set color-scheme on the document root to match the active theme.
   * Enables native dark scrollbars and CSS light-dark() function.
   *
   * Disable only when you need the browser canvas to stay transparent in hosts
   * that render widgets over a differently-themed background.
   * @default true
   */
  colorScheme?: boolean;
}

/**
 * Unified provider component that combines all common React setup for mcp-use widgets.
 *
 * Includes:
 * - StrictMode (always)
 * - ThemeProvider (always)
 * - WidgetControls (if debugger={true} or viewControls is set)
 * - ErrorBoundary (always)
 * - Auto-sizing (if autoSize={true})
 *
 * @example
 * ```tsx
 * <McpUseProvider debugger viewControls autoSize>
 *   <div>My widget content</div>
 * </McpUseProvider>
 * ```
 */
export function McpUseProvider({
  children,
  debugger: enableDebugger = false,
  viewControls = false,
  autoSize = true,
  colorScheme = true,
}: McpUseProviderProps) {
  const [containerElement, setContainerElement] =
    useState<HTMLDivElement | null>(null);
  const lastHeightRef = useRef<number>(0);
  const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const notificationInProgressRef = useRef<boolean>(false);

  // Notify host about height changes via MCP Apps.
  const notifyHeight = useCallback((height: number) => {
    if (typeof window === "undefined") return;

    notificationInProgressRef.current = true;

    if (window === window.parent) {
      notificationInProgressRef.current = false;
      return;
    }

    try {
      const bridge = getMcpAppsBridge();
      bridge.sendSizeChanged({ height });
      console.log("[McpUseProvider] Sent size-changed notification:", height);
    } catch (error) {
      console.error(
        "[McpUseProvider] Failed to notify size change (MCP Apps):",
        error
      );
    } finally {
      notificationInProgressRef.current = false;
    }
  }, []);

  // Debounced height notification with threshold to prevent feedback loops
  // Uses longer debounce to wait for animations to settle
  const debouncedNotifyHeight = useCallback(
    (height: number) => {
      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
      }
      debounceTimeoutRef.current = setTimeout(() => {
        // Always notify zero height and for positive heights only notify if changed by more than threshold
        const heightDiff = Math.abs(height - lastHeightRef.current);
        if (height === 0 || heightDiff >= MIN_HEIGHT_CHANGE_PX) {
          lastHeightRef.current = height;
          notifyHeight(height);
        }
      }, HEIGHT_DEBOUNCE_MS);
    },
    [notifyHeight]
  );

  // Set up ResizeObserver for auto-sizing
  useEffect(() => {
    if (!autoSize) {
      console.log("[McpUseProvider] autoSize is disabled");
      return;
    }

    if (!containerElement) {
      console.log("[McpUseProvider] No container element found for autoSize");
      return;
    }

    if (typeof ResizeObserver === "undefined") {
      console.log("[McpUseProvider] ResizeObserver not available");
      return;
    }

    console.log("[McpUseProvider] Setting up ResizeObserver for autoSize");

    const observer = new ResizeObserver((entries) => {
      // Skip if notification is in progress to prevent feedback loop
      if (notificationInProgressRef.current) {
        console.log(
          "[McpUseProvider] Skipping resize - notification in progress"
        );
        return;
      }

      for (const entry of entries) {
        const height = entry.contentRect.height;
        // Use scrollHeight as fallback for more accurate intrinsic height
        const scrollHeight = entry.target.scrollHeight;
        const intrinsicHeight = Math.max(height, scrollHeight);
        console.log("[McpUseProvider] ResizeObserver fired:", {
          height,
          scrollHeight,
          intrinsicHeight,
        });
        debouncedNotifyHeight(intrinsicHeight);
      }
    });

    observer.observe(containerElement);

    // Initial measurement
    const initialHeight = Math.max(
      containerElement.offsetHeight,
      containerElement.scrollHeight
    );
    console.log("[McpUseProvider] Initial height measurement:", initialHeight);
    if (initialHeight > 0) {
      debouncedNotifyHeight(initialHeight);
    }

    return () => {
      console.log("[McpUseProvider] Cleaning up ResizeObserver");
      observer.disconnect();
      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
        debounceTimeoutRef.current = null;
      }
      // Reset notification flag
      notificationInProgressRef.current = false;
    };
  }, [autoSize, containerElement, debouncedNotifyHeight]);

  // Build the component tree with conditional wrappers
  let content: React.ReactNode = children;

  // ErrorBoundary is always the innermost wrapper
  content = <ErrorBoundary>{content}</ErrorBoundary>;

  // WidgetControls wraps ErrorBoundary if debugger is enabled or viewControls is set
  if (enableDebugger || viewControls) {
    content = (
      <WidgetControls debugger={enableDebugger} viewControls={viewControls}>
        {content}
      </WidgetControls>
    );
  }

  // ThemeProvider wraps WidgetControls
  content = <ThemeProvider colorScheme={colorScheme}>{content}</ThemeProvider>;

  // Wrap in container div for auto-sizing if enabled
  if (autoSize) {
    const containerStyle: React.CSSProperties = {
      width: "100%",
      minHeight: 0,
    };
    content = (
      <div ref={setContainerElement} style={containerStyle}>
        {content}
      </div>
    );
  }

  // StrictMode is the outermost wrapper
  return <StrictMode>{content}</StrictMode>;
}
