'use client';
import * as React from 'react';
import { AppState } from 'react-native';
import { useOnMount } from '../../hooks/useOnMount';
import { useRefWithInit } from '../../hooks/useRefWithInit';
import { useIsoLayoutEffect } from '../../hooks/useIsoLayoutEffect';
import { ToastContext } from './ToastProviderContext';
import type { ToastManager } from '../createToastManager';
import { ToastStore } from '../store';

/**
 * Provides a context for creating and managing toasts.
 */
export const ToastProvider: React.FC<ToastProvider.Props> = function ToastProvider(props) {
  const { children, timeout = 5000, limit = 3, toastManager } = props;

  const store = useRefWithInit(
    () =>
      new ToastStore({
        timeout,
        limit,
        toasts: [],
        pressed: false,
        focused: false,
        isAppActive: AppState.currentState !== 'background',
        viewportOrigin: { x: 0, y: 0 },
      }),
  ).current;

  useOnMount(store.disposeEffect);

  // Upstream pauses toast timers while the window is unfocused. The React Native
  // counterpart is the app being backgrounded — a toast must not spend its five
  // seconds where nobody can see it.
  React.useEffect(() => {
    const subscription = AppState.addEventListener('change', (nextAppState) => {
      const isAppActive = nextAppState !== 'background';
      store.set('isAppActive', isAppActive);

      if (isAppActive) {
        if (!store.select('expanded')) {
          store.resumeTimers();
        }
      } else {
        store.pauseTimers();
      }
    });

    return () => subscription.remove();
  }, [store]);

  React.useEffect(
    function subscribeToToastManager() {
      if (!toastManager) {
        return undefined;
      }

      return toastManager[' subscribe'](({ action, options }) => {
        const id = options.id;

        if (action === 'promise' && options.promise) {
          store.promiseToast(options.promise, options);
        } else if (action === 'update' && id) {
          store.updateToast(id, options);
        } else if (action === 'close') {
          store.closeToast(id);
        } else {
          store.addToast(options);
        }
      });
    },
    [store, toastManager],
  );

  // `limit` needs custom syncing because changing it must also recompute each
  // toast's `limited` flag; `useSyncedValues` would only update the raw value.
  useIsoLayoutEffect(() => {
    store.syncProviderProps(timeout, limit);
  }, [store, timeout, limit]);

  return <ToastContext.Provider value={store}>{children}</ToastContext.Provider>;
};

export interface ToastProviderState {}

export interface ToastProviderProps {
  children?: React.ReactNode;
  /**
   * The default amount of time (in ms) before a toast is auto dismissed.
   * A value of `0` will prevent the toast from being dismissed automatically.
   * @default 5000
   */
  timeout?: number | undefined;
  /**
   * The maximum number of toasts that can be displayed at once.
   * When the limit is exceeded, the oldest toasts are marked `limited` on their
   * state rather than removed, so they can be hidden or animated out.
   * @default 3
   */
  limit?: number | undefined;
  /**
   * A global manager for toasts to use outside of a React component.
   */
  toastManager?: ToastManager | undefined;
}

export namespace ToastProvider {
  export type State = ToastProviderState;
  export type Props = ToastProviderProps;
}
