"use strict";

import React, { useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef } from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import { BottomSheet } from "../bottom-sheet/index.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";
/**
 * Native variant of `<Dialog>`.
 *
 * Uses bloom's own `BottomSheet` in `detached` mode as the underlying
 * surface — a floating, dynamically-sized, content-hugging card that
 * gracefully degrades from sheet (phone) to centered card (tablet, via the
 * 500px max-width cap).
 *
 * The component accepts three rendering modes simultaneously:
 *
 *   1. Declarative — `title`, `description`, `actions`. Bloom renders a
 *      standard confirm/destructive/cancel layout.
 *   2. Custom children — caller passes JSX, bloom renders the chrome (title
 *      + close behaviour) and the children fill the body.
 *   3. Pure children — no `title`/`description`/`actions`; caller owns
 *      every pixel.
 *
 * All three share the same dismissal semantics: tapping the backdrop, the
 * drag handle, swiping down, or `control.close()` runs the queued close
 * callbacks once the sheet's exit animation has settled.
 */
export function Dialog({
  control,
  onClose,
  testID,
  title,
  description,
  actions,
  style,
  label,
  children
}) {
  const theme = useTheme();
  const ref = useRef(null);
  const closeCallbacks = useRef([]);
  const titleId = useId();
  const descriptionId = useId();

  // Drain queued close callbacks atomically — capturing the list and
  // resetting it before invocation ensures a callback that synchronously
  // re-opens the dialog (and queues fresh callbacks) does not see the old
  // ones replayed against the new session.
  const callQueuedCallbacks = useCallback(() => {
    const queued = closeCallbacks.current;
    closeCallbacks.current = [];
    for (const cb of queued) {
      try {
        cb();
      } catch (e) {
        if (typeof console !== 'undefined' && console.error) {
          console.error('Dialog close callback error:', e);
        }
      }
    }
  }, []);
  const open = useCallback(() => {
    ref.current?.present();
  }, []);
  const close = useCallback(cb => {
    if (typeof cb === 'function') {
      closeCallbacks.current.push(cb);
    }
    ref.current?.dismiss();
  }, []);
  const handleDismiss = useCallback(() => {
    callQueuedCallbacks();
    onClose?.();
  }, [callQueuedCallbacks, onClose]);
  useImperativeHandle(control.ref, () => ({
    open,
    close
  }), [open, close]);
  const context = useMemo(() => ({
    close,
    isWithinDialog: true
  }), [close]);
  const sheetStyle = useMemo(() => ({
    maxWidth: 500,
    backgroundColor: theme.colors.background,
    // All four corners rounded — bloom's BottomSheet defaults to top-only
    // radius in flush mode, but we use `detached` so the whole card is
    // floating and rounded uniformly.
    borderRadius: 20
  }), [theme.colors.background]);
  return /*#__PURE__*/_jsx(BottomSheet, {
    ref: ref,
    onDismiss: handleDismiss,
    enablePanDownToClose: true,
    detached: true
    // Stronger dim when a Dialog is stacked over another sheet so the
    // underlying sheet's handle/content doesn't bleed through.
    ,
    backdropOpacity: 0.7,
    style: sheetStyle,
    children: /*#__PURE__*/_jsx(Context.Provider, {
      value: context,
      children: /*#__PURE__*/_jsxs(View, {
        testID: testID,
        accessibilityLabel: label,
        "aria-labelledby": title ? titleId : undefined,
        "aria-describedby": description ? descriptionId : undefined,
        style: [
        // Detached BottomSheet already adds `marginBottom: insets.bottom + 16`
        // to the sheet container — the floating card sits ABOVE the
        // system gesture bar, so we don't add `insets.bottom` here.
        {
          paddingTop: 20,
          paddingHorizontal: 20,
          paddingBottom: 20
        }, {
          backgroundColor: theme.colors.background
        }, 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') {
      // Cancel always dismisses; consumer's onPress (rare) runs after.
      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 2 -- TS exhaustiveness check guards this branch */
    default:
      {
        const _exhaustive = color;
        return {
          background: colors.primary,
          foreground: colors.primaryForeground
        };
      }
  }
}

/**
 * Helper used by the imperative `alert()` API. Mounts a `<Dialog>` against
 * a fresh control and presents it immediately. `onResolve` is invoked
 * exactly once when the dialog finishes closing (regardless of how the
 * dismissal happened).
 *
 * Kept private to the dialog module — `alert()` is the public surface.
 */
export function AutoMountedDialog({
  title,
  description,
  actions,
  onResolve
}) {
  const control = useDialogControl();

  // `control` is referentially stable for the lifetime of the component
  // (memoised by `useDialogControl` on `[id]`), so this effect runs exactly
  // once per mount — present-on-mount semantics for an `alert()` call
  // without re-presenting on subsequent renders.
  useEffect(() => {
    control.open();
  }, [control]);
  return /*#__PURE__*/_jsx(Dialog, {
    control: control,
    title: title,
    description: description,
    actions: actions,
    onClose: onResolve
  });
}
//# sourceMappingURL=Dialog.js.map