"use strict";

import React, { createContext, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { Pressable, Text, TouchableOpacity, View } from 'react-native';
import { RemoveScrollBar } from 'react-remove-scroll-bar';
import { Portal } from "../portal/index.web.js";
import { useTheme } from "../theme/use-theme.js";
import { Context, useDialogContext, useDialogControl } from "./context.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const FADE_OUT_DURATION = 150;
const stopPropagation = e => e.stopPropagation();
const ClosingContext = /*#__PURE__*/createContext(false);

/**
 * Web variant of `<Dialog>`.
 *
 * A centered modal card rendered into the bloom Portal at the end of
 * `document.body`. Same prop API as native — `title`, `description`,
 * `actions`, or arbitrary `children` — so call sites are platform
 * agnostic.
 *
 * Accessibility: the panel has `role="dialog"` and the `title`/`description`
 * props (when provided) become the `aria-labelledby` / `aria-describedby`
 * targets. Pressing the backdrop dismisses. Pressing `Escape` dismisses.
 * Focus is locked inside the dialog while it's open.
 */
export function Dialog({
  control,
  onClose,
  testID,
  title,
  description,
  actions,
  style,
  label,
  children
}) {
  const [isOpen, setIsOpen] = useState(false);
  const [isClosing, setIsClosing] = useState(false);
  const closeCallbacksRef = useRef([]);
  const open = useCallback(() => {
    setIsClosing(false);
    setIsOpen(true);
  }, []);
  const close = useCallback(cb => {
    if (typeof cb === 'function') {
      closeCallbacksRef.current.push(cb);
    }
    setIsClosing(true);
  }, []);
  useEffect(() => {
    if (!isClosing) return;
    const timer = setTimeout(() => {
      setIsOpen(false);
      setIsClosing(false);
      const queued = closeCallbacksRef.current;
      closeCallbacksRef.current = [];
      for (const cb of queued) {
        try {
          cb();
        } catch (e) {
          if (typeof console !== 'undefined' && console.error) {
            console.error('Dialog close callback error:', e);
          }
        }
      }
      onClose?.();
    }, FADE_OUT_DURATION);
    return () => clearTimeout(timer);
  }, [isClosing, onClose]);

  // Escape-to-close while open. The listener is intentionally scoped to the
  // open lifetime so stacked dialogs don't fight for the keydown — the
  // top-most one wins via document-level event order.
  useEffect(() => {
    if (!isOpen || typeof document === 'undefined') return;
    const handler = e => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        close();
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [close, isOpen]);
  useImperativeHandle(control.ref, () => ({
    open,
    close
  }), [open, close]);
  const context = useMemo(() => ({
    close,
    isWithinDialog: true
  }), [close]);
  if (!isOpen) return null;
  return /*#__PURE__*/_jsx(Portal, {
    children: /*#__PURE__*/_jsx(Context.Provider, {
      value: context,
      children: /*#__PURE__*/_jsxs(ClosingContext.Provider, {
        value: isClosing,
        children: [/*#__PURE__*/_jsx(RemoveScrollBar, {}), /*#__PURE__*/_jsxs(Pressable, {
          onPress: () => close()
          // `pointerEvents: 'auto'` opts back in from the Portal root's
          // `pointer-events: none`, which is set so the idle portal
          // doesn't intercept clicks on the underlying app.
          ,
          style: {
            position: 'fixed',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            zIndex: 50,
            alignItems: 'center',
            justifyContent: 'center',
            paddingHorizontal: 20,
            pointerEvents: 'auto'
          },
          children: [/*#__PURE__*/_jsx(DialogBackdrop, {
            isClosing: isClosing
          }), /*#__PURE__*/_jsx(DialogPanel, {
            testID: testID,
            label: label,
            title: title,
            description: description,
            actions: actions,
            style: style,
            isClosing: isClosing,
            children: children
          })]
        })]
      })
    })
  });
}
function DialogPanel({
  testID,
  label,
  title,
  description,
  actions,
  style,
  isClosing,
  children
}) {
  const theme = useTheme();
  const titleId = useId();
  const descriptionId = useId();
  return /*#__PURE__*/_jsxs(View, {
    role: "dialog",
    "aria-label": label,
    "aria-labelledby": title ? titleId : undefined,
    "aria-describedby": description ? descriptionId : undefined,
    testID: testID,
    onStartShouldSetResponder: () => true,
    onResponderRelease: stopPropagation,
    onClick: stopPropagation,
    style: [{
      position: 'relative',
      borderRadius: 20,
      width: '100%',
      maxWidth: 480,
      backgroundColor: theme.colors.background,
      borderWidth: 1,
      borderColor: theme.isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)',
      shadowColor: '#000',
      shadowOpacity: theme.isDark ? 0.4 : 0.1,
      shadowRadius: 30,
      shadowOffset: {
        width: 0,
        height: 4
      },
      padding: 20,
      zIndex: 60
    }, isClosing ? {
      animation: `bloomDialogZoomFadeOut ease-in ${FADE_OUT_DURATION}ms forwards`
    } : {
      animation: 'bloomDialogZoomFadeIn cubic-bezier(0.16, 1, 0.3, 1) 0.3s'
    }, style],
    children: [title ? /*#__PURE__*/_jsx(Text, {
      nativeID: titleId,
      style: {
        fontSize: 22,
        fontWeight: '600',
        color: theme.colors.text,
        paddingBottom: description ? 4 : 16,
        lineHeight: 30
      },
      children: title
    }) : null, description ? /*#__PURE__*/_jsx(Text, {
      nativeID: descriptionId,
      style: {
        fontSize: 16,
        color: theme.colors.textSecondary,
        paddingBottom: 16,
        lineHeight: 22
      },
      children: description
    }) : null, children, actions && actions.length > 0 ? /*#__PURE__*/_jsx(ActionRow, {
      actions: actions
    }) : null]
  });
}
function ActionRow({
  actions
}) {
  return /*#__PURE__*/_jsx(View, {
    style: {
      width: '100%',
      gap: 8,
      justifyContent: 'flex-end'
    },
    children: actions.map((action, idx) => /*#__PURE__*/_jsx(ActionButton, {
      action: action
    }, `${action.label}-${idx}`))
  });
}
function ActionButton({
  action
}) {
  const {
    close
  } = useDialogContext();
  const theme = useTheme();
  const color = action.color ?? 'default';
  const shouldCloseOnPress = action.shouldCloseOnPress ?? true;
  const {
    background,
    foreground
  } = getActionPalette(color, theme.colors);
  const handlePress = useCallback(e => {
    const onPress = action.onPress;
    if (color === 'cancel') {
      close(onPress ? () => onPress(e) : undefined);
      return;
    }
    if (shouldCloseOnPress) {
      close(onPress ? () => onPress(e) : undefined);
    } else {
      onPress?.(e);
    }
  }, [action.onPress, close, color, shouldCloseOnPress]);
  return /*#__PURE__*/_jsx(TouchableOpacity, {
    style: {
      borderRadius: 9999,
      alignItems: 'center',
      justifyContent: 'center',
      backgroundColor: background,
      opacity: action.disabled ? 0.5 : 1,
      paddingVertical: 12,
      paddingHorizontal: 24
    },
    onPress: handlePress,
    disabled: action.disabled,
    activeOpacity: 0.7,
    testID: action.testID,
    children: /*#__PURE__*/_jsx(Text, {
      style: {
        fontSize: 16,
        fontWeight: '500',
        color: foreground
      },
      children: action.label
    })
  });
}
function getActionPalette(color, colors) {
  switch (color) {
    case 'destructive':
      return {
        background: colors.negative,
        foreground: colors.negativeForeground
      };
    case 'cancel':
      return {
        background: colors.contrast50,
        foreground: colors.text
      };
    case 'default':
      return {
        background: colors.primary,
        foreground: colors.primaryForeground
      };
    /* c8 ignore next 3 -- TS exhaustiveness guard */
    default:
      {
        const _exhaustive = color;
        return {
          background: colors.primary,
          foreground: colors.primaryForeground
        };
      }
  }
}
function DialogBackdrop({
  isClosing
}) {
  const style = [{
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0,0,0,0.8)'
  }, isClosing ? {
    animation: `bloomDialogFadeOut ease-in ${FADE_OUT_DURATION}ms forwards`
  } : {
    animation: 'bloomDialogFadeIn ease-out 0.15s'
  }];
  return /*#__PURE__*/_jsx(View, {
    style: style
  });
}

/**
 * Inline imperative dialog used by `alert()`. Mounts and immediately
 * presents; resolves the host's `onResolve` once the dialog has finished
 * its exit animation.
 */
export function AutoMountedDialog({
  title,
  description,
  actions,
  onResolve
}) {
  const control = useDialogControl();
  useEffect(() => {
    control.open();
  }, [control]);
  return /*#__PURE__*/_jsx(Dialog, {
    control: control,
    title: title,
    description: description,
    actions: actions,
    onClose: onResolve
  });
}

/**
 * CSS keyframes required for web dialog animations.
 * Consumers should inject this string into a <style> tag or their global CSS:
 *
 * ```css
 * @keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
 * @keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }
 * @keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
 * @keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
 * ```
 */
export const BLOOM_DIALOG_CSS = `
@keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
@keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
`;
//# sourceMappingURL=Dialog.web.js.map