/**
 * AgentKitProvider — React context provider that wraps the app to enable AgentKit.
 *
 * This is the main integration point for developers. Wrap your app with this
 * provider and the library handles everything: element introspection, command
 * server, and action execution.
 *
 * Usage:
 * ```tsx
 * import { AgentKitProvider } from 'react-native-agentkit';
 *
 * function App() {
 *   return (
 *     <AgentKitProvider debug={__DEV__}>
 *       <YourApp />
 *     </AgentKitProvider>
 *   );
 * }
 * ```
 */

import React, {
  createContext,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';
import { View, InteractionManager } from 'react-native';
import type { AgentKitConfig, ElementInfo, ElementHandlers } from './types';
import { DEFAULT_CONFIG } from './types';
import { ElementRegistry } from './ElementRegistry';
import { BridgeServer } from './WSServer';
import { RelayTransport } from './RelayTransport';
import { introspect } from './Introspector';
import { setDebugEnabled, logInfo, logDebug } from './logger';
import {
  installNativeDialogInterceptor,
  uninstallNativeDialogInterceptor,
} from './NativeDialogInterceptor';

// ─── Context ─────────────────────────────────────────────────────────────────

export interface AgentKitContextValue {
  /** Whether the bridge server is currently running */
  isRunning: boolean;
  /** Whether the relay transport is connected */
  isRelayConnected: boolean;
  /** Number of registered elements */
  elementCount: number;
  /** Manually register an element */
  registerElement: (
    id: string,
    info: Omit<ElementInfo, 'id'>,
    ref: React.RefObject<any>,
    handlers?: ElementHandlers
  ) => string;
  /** Manually unregister an element */
  unregisterElement: (id: string) => void;
  /** Force a re-scan of the component tree */
  rescan: () => void;
}

export const AgentKitContext = createContext<AgentKitContextValue>({
  isRunning: false,
  isRelayConnected: false,
  elementCount: 0,
  registerElement: () => '',
  unregisterElement: () => {},
  rescan: () => {},
});

// ─── Props ───────────────────────────────────────────────────────────────────

export interface AgentKitProviderProps {
  children: React.ReactNode;
  /** WebSocket server port (default: 8347) */
  port?: number;
  /** Enable debug logging (default: false) */
  debug?: boolean;
  /** Element scan interval in ms (default: 1000) */
  scanInterval?: number;
  /** Include non-interactive elements in scans (default: false) */
  includeNonInteractive?: boolean;
  /** Auto-include screen state in command responses (default: true) */
  includeScreenState?: boolean;
  /** Disable the bridge entirely (e.g. in production) */
  enabled?: boolean;
  /** Cloud relay server URL for production (e.g. "ws://relay.example.com:8347") */
  relayUrl?: string;
  /** Channel ID for relay pairing (default: "default") */
  channelId?: string;
  /** Shared secret for relay channel authentication */
  channelSecret?: string;
  /** Navigation container ref for back navigation support */
  navigationRef?: React.RefObject<any>;
}

/** Module-level navigation ref for CommandHandler to access */
let _navigationRef: React.RefObject<any> | null = null;
export function getNavigationRef(): React.RefObject<any> | null {
  return _navigationRef;
}

// ─── Provider Component ──────────────────────────────────────────────────────

export function AgentKitProvider({
  children,
  port,
  debug,
  scanInterval,
  includeNonInteractive,
  includeScreenState,
  enabled = true,
  relayUrl,
  channelId,
  channelSecret,
  navigationRef,
}: AgentKitProviderProps) {
  // Store navigation ref for CommandHandler to access
  _navigationRef = navigationRef ?? null;
  const rootRef = useRef<any>(null);
  const serverRef = useRef<BridgeServer | null>(null);
  const relayRef = useRef<RelayTransport | null>(null);
  const scanTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const [isRunning, setIsRunning] = useState(false);
  const [isRelayConnected, setIsRelayConnected] = useState(false);
  const [elementCount, setElementCount] = useState(0);

  // Build config from props
  const config: AgentKitConfig = {
    port: port ?? DEFAULT_CONFIG.port,
    debug: debug ?? DEFAULT_CONFIG.debug,
    scanInterval: scanInterval ?? DEFAULT_CONFIG.scanInterval,
    includeNonInteractive:
      includeNonInteractive ?? DEFAULT_CONFIG.includeNonInteractive,
    includeScreenState: includeScreenState ?? DEFAULT_CONFIG.includeScreenState,
    relayUrl,
    channelId: channelId ?? DEFAULT_CONFIG.channelId,
    channelSecret,
  };

  // ─── Introspection scan ──────────────────────────────────────────────

  const runScan = useCallback(() => {
    if (!rootRef.current) return;

    InteractionManager.runAfterInteractions(() => {
      const elements = introspect(rootRef, config.includeNonInteractive);
      ElementRegistry.bulkUpdate(elements);
      // Use registry size (includes both introspected + manually-registered)
      setElementCount(ElementRegistry.size);
      logDebug(`Scan complete: ${ElementRegistry.size} elements`);
    });
  }, [config.includeNonInteractive]);

  // ─── Server lifecycle ────────────────────────────────────────────────

  useEffect(() => {
    if (!enabled) return;

    setDebugEnabled(config.debug);

    // Create and start the server
    const server = new BridgeServer(config);
    serverRef.current = server;
    server.start();
    setIsRunning(true);

    // Install native dialog interceptor (alerts + permissions)
    // Only suppresses native dialogs when an agent is actually connected
    installNativeDialogInterceptor(
      () => relayRef.current?.isConnected ?? false
    );

    logInfo('AgentKitProvider mounted');

    // Start relay transport if configured
    let relay: RelayTransport | null = null;
    if (config.relayUrl) {
      relay = new RelayTransport({
        relayUrl: config.relayUrl,
        channelId: config.channelId,
        channelSecret: config.channelSecret,
        onMessage: (msg) => server.processMessage(msg),
        onStatusChange: setIsRelayConnected,
      });
      relayRef.current = relay;
      relay.start();
      logInfo(`Relay transport enabled → ${config.relayUrl}`);
    }

    // Start periodic scanning
    // Delay the first scan to ensure the tree is rendered
    const initialScanTimeout = setTimeout(() => {
      runScan();

      // Then scan periodically
      scanTimerRef.current = setInterval(runScan, config.scanInterval);
    }, 500);

    return () => {
      clearTimeout(initialScanTimeout);
      if (scanTimerRef.current) {
        clearInterval(scanTimerRef.current);
        scanTimerRef.current = null;
      }
      if (relay) {
        relay.stop();
        relayRef.current = null;
        setIsRelayConnected(false);
      }
      server.stop();
      serverRef.current = null;
      setIsRunning(false);
      ElementRegistry.clear();
      uninstallNativeDialogInterceptor();
      logInfo('AgentKitProvider unmounted');
    };
  }, [enabled]); // eslint-disable-line react-hooks/exhaustive-deps

  // ─── Context value ───────────────────────────────────────────────────

  const registerElement = useCallback(
    (
      id: string,
      info: Omit<ElementInfo, 'id'>,
      ref: React.RefObject<any>,
      handlers?: ElementHandlers
    ): string => {
      const registeredId = ElementRegistry.register(id, info, ref, handlers);
      setElementCount(ElementRegistry.size);
      return registeredId;
    },
    []
  );

  const unregisterElement = useCallback((id: string) => {
    ElementRegistry.unregister(id);
    setElementCount(ElementRegistry.size);
  }, []);

  const rescan = useCallback(() => {
    runScan();
  }, [runScan]);

  const contextValue: AgentKitContextValue = {
    isRunning,
    isRelayConnected,
    elementCount,
    registerElement,
    unregisterElement,
    rescan,
  };

  // If disabled, just render children without the bridge
  if (!enabled) {
    return <>{children}</>;
  }

  return (
    <AgentKitContext.Provider value={contextValue}>
      <View ref={rootRef} style={{ flex: 1 }} collapsable={false}>
        {children}
      </View>
    </AgentKitContext.Provider>
  );
}
