import type { ReactNode } from 'react';
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import { Platform, View } from 'react-native';
import { SafeAreaInsetsContext, type EdgeInsets } from 'react-native-safe-area-context';
import { FullWindowOverlay } from '@cdx-ui/primitives';
import type { ToastInsets } from './types';

const ZERO_INSETS: EdgeInsets = { top: 0, bottom: 0, left: 0, right: 0 };

interface InsetsContainerProps {
  /**
   * When true, uses a regular View instead of FullWindowOverlay on iOS.
   * Enables element inspector but toasts won't appear above native modals.
   * @default false
   */
  disableFullWindowOverlay: boolean;
  /**
   * Controls whether VoiceOver treats the overlay window as a modal container.
   * When `false`, VoiceOver can still access elements behind the overlay.
   * When `true`, VoiceOver is restricted to elements inside the overlay.
   * @default false
   * @platform ios
   * @unstable This prop maps directly to the native `accessibilityViewIsModal`
   * on the container view and may change in a future react-native-screens release.
   */
  unstable_accessibilityContainerViewIsModal?: boolean;
  /**
   * Optional inset values for all edges
   * If not provided, defaults to platform-specific values:
   * - iOS: safe area insets + 0px (top), + 6px (bottom), + 12px (left/right)
   * - Android: safe area insets + 12px (all edges)
   */
  insets?: ToastInsets;
  /**
   * Accessible label for the toast notification region on web.
   * @default 'Notifications'
   * @platform web
   */
  announceLabel?: string;
  /**
   * Custom wrapper function to wrap the toast content
   * Receives children and should return a component that wraps them
   * The wrapper should apply flex: 1 (via className or style) to ensure proper layout
   * Can be any component wrapper - KeyboardAvoidingView, View, or any custom component
   */
  contentWrapper?: (children: ReactNode) => React.ReactElement;
  /**
   * Pause all auto-dismiss timers (WCAG 2.2.1).
   */
  pauseAll: () => void;
  /**
   * Resume all auto-dismiss timers.
   */
  resumeAll: () => void;
  /**
   * Hide the front-most toast. Used by the Escape key handler on web.
   */
  hide: () => void;
  /**
   * Children to render inside the container
   */
  children: ReactNode;
}

/**
 * Container component that applies inset padding to position toasts
 * away from screen edges and safe areas
 *
 * Combines custom insets with safe area insets:
 * - If custom inset is provided, it overrides safe area + default padding
 * - If not provided, uses platform-specific defaults:
 *   - iOS: safe area inset + 0px for top, + 6px for bottom, + 12px for left/right
 *   - Android: safe area inset + 12px for all edges
 *
 * On web, wires pointer/focus/visibility listeners to pause/resume timers
 * per WCAG 2.2.1 (keyboard/pointer/hidden-tab pausing).
 */
export function InsetsContainer({
  insets,
  announceLabel = 'Notifications',
  contentWrapper,
  children,
  disableFullWindowOverlay,
  unstable_accessibilityContainerViewIsModal,
  pauseAll,
  resumeAll,
  hide,
}: Readonly<InsetsContainerProps>) {
  const safeAreaInsets = useContext(SafeAreaInsetsContext) ?? ZERO_INSETS;

  const finalInsets = useMemo(() => {
    return {
      top: insets?.top ?? safeAreaInsets.top + (Platform.OS === 'ios' ? 0 : 12),
      bottom: insets?.bottom ?? safeAreaInsets.bottom + (Platform.OS === 'ios' ? 6 : 12),
      left: insets?.left ?? safeAreaInsets.left + 12,
      right: insets?.right ?? safeAreaInsets.right + 12,
    };
  }, [safeAreaInsets, insets]);

  // Track pause sources independently so overlapping pointer + focus
  // don't accidentally resume while the other source is still active.
  const pauseSourcesRef = useRef<Set<string>>(new Set());
  const pauseAllRef = useRef(pauseAll);
  const resumeAllRef = useRef(resumeAll);
  pauseAllRef.current = pauseAll;
  resumeAllRef.current = resumeAll;

  const addPauseSource = useCallback((source: string) => {
    const wasEmpty = pauseSourcesRef.current.size === 0;
    pauseSourcesRef.current.add(source);
    if (wasEmpty) pauseAllRef.current();
  }, []);

  const removePauseSource = useCallback((source: string) => {
    pauseSourcesRef.current.delete(source);
    if (pauseSourcesRef.current.size === 0) resumeAllRef.current();
  }, []);

  // Ref for the container so we can attach native DOM listeners on web.
  const containerRef = useRef<View | null>(null);
  const hideRef = useRef(hide);
  hideRef.current = hide;

  // Web-only: attach pointer, focus, visibility, and keyboard listeners
  // via native DOM APIs to avoid RNW making the container focusable
  // (RNW adds tabIndex when React onFocus/onBlur handlers are present).
  useEffect(() => {
    if (Platform.OS !== 'web') return;

    const node = containerRef.current as unknown as HTMLElement | null;
    const sources = pauseSourcesRef.current;

    const handleVisibilityChange = () => {
      if (document.hidden) {
        addPauseSource('visibility');
      } else {
        removePauseSource('visibility');
      }
    };

    // The container is `absolute inset-0` (full viewport). Using
    // pointerenter/pointerleave here would pause as soon as the cursor
    // is anywhere on the page — far too aggressive.  Instead use
    // pointerover/pointerout (which bubble) and gate on whether the
    // pointer is actually over a toast element (identified by the
    // `data-state` attribute emitted on every toast wrapper).
    const isInsideToast = (el: EventTarget | null): boolean => {
      if (!el || !(el instanceof HTMLElement)) return false;
      return !!el.closest('[data-state]');
    };

    const handlePointerOver = (e: PointerEvent) => {
      if (isInsideToast(e.target)) addPauseSource('pointer');
    };
    const handlePointerOut = (e: PointerEvent) => {
      if (!isInsideToast(e.relatedTarget)) removePauseSource('pointer');
    };

    const handleFocusIn = (e: FocusEvent) => {
      if (isInsideToast(e.target)) addPauseSource('focus');
    };
    const handleFocusOut = (e: FocusEvent) => {
      if (!isInsideToast(e.relatedTarget)) removePauseSource('focus');
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        event.stopPropagation();
        hideRef.current();
      }
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);

    if (node && typeof node.addEventListener === 'function') {
      node.addEventListener('pointerover', handlePointerOver);
      node.addEventListener('pointerout', handlePointerOut);
      node.addEventListener('focusin', handleFocusIn);
      node.addEventListener('focusout', handleFocusOut);
      node.addEventListener('keydown', handleKeyDown);
    }

    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
      if (node && typeof node.removeEventListener === 'function') {
        node.removeEventListener('pointerover', handlePointerOver);
        node.removeEventListener('pointerout', handlePointerOut);
        node.removeEventListener('focusin', handleFocusIn);
        node.removeEventListener('focusout', handleFocusOut);
        node.removeEventListener('keydown', handleKeyDown);
      }
      if (sources.size > 0) {
        sources.clear();
        resumeAllRef.current();
      }
    };
  }, [addPauseSource, removePauseSource]);

  const content = (
    <View
      ref={containerRef}
      className="absolute inset-0 pointer-events-box-none"
      role="region"
      aria-label={announceLabel}
      {...(Platform.OS === 'web' && {
        'aria-live': 'polite',
        'aria-atomic': false,
      })}
      style={{
        paddingTop: finalInsets.top,
        paddingBottom: finalInsets.bottom,
        paddingLeft: finalInsets.left,
        paddingRight: finalInsets.right,
      }}
    >
      {contentWrapper ? contentWrapper(children) : children}
    </View>
  );

  if (Platform.OS !== 'ios') {
    return content;
  }

  return (
    <FullWindowOverlay
      disableFullWindowOverlay={disableFullWindowOverlay}
      unstable_accessibilityContainerViewIsModal={unstable_accessibilityContainerViewIsModal}
    >
      {content}
    </FullWindowOverlay>
  );
}
