import { createContext, PropsWithChildren, ReactElement, useContext, useEffect, useState } from 'react';
import { nativeEventEmitter } from '../native-event-emitter';

const VisibilityChangedContext = createContext<boolean | undefined>(undefined);

/**
 * @name VisibilityChangedProvider
 * @kind function
 * @description
 * 토스 앱 React Native 화면이 보이는지 여부를 관리하는 Provider예요.
 * 토스 앱의 `visibilityChanged` 이벤트를 구독해서 화면이 보이는지 여부를 감지하고 관리해요.
 *
 * @param {ReactNode | undefined} children - 화면이 보이는지 여부를 확인하는 자식 컴포넌트예요.
 * @param {boolean} isVisible - 화면이 보이는지 여부를 나타내는 `boolean` 값이에요.
 * @returns {ReactElement} - `VisibilityChangedContext.Provider`로 감싼 React Provider 컴포넌트예요.
 * @example
 * ```typescript
 * export function VisibilityProvider({ isVisible, children }: Props) {
 *   return (
 *     <VisibilityChangedProvider isVisible={isVisible}>
 *       {children}
 *     </VisibilityChangedProvider>
 *   );
 * }
 * ```
 */
export function VisibilityChangedProvider({
  children,
  isVisible,
}: PropsWithChildren<{ isVisible: boolean }>): ReactElement {
  const [visible, setVisible] = useState(isVisible);

  useEffect(() => {
    const subscription = nativeEventEmitter.addListener('visibilityChanged', (nextVisible) => {
      setVisible(nextVisible);
    });

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

  return <VisibilityChangedContext.Provider value={visible}>{children}</VisibilityChangedContext.Provider>;
}

/**
 * @name useVisibilityChanged
 * @category Hooks
 * @kind function
 * @description
 * 토스 앱 React Native 화면이 보이는지 여부를 반환하는 Hook이에요.
 * @returns {boolean} - 화면이 보이는지 여부를 반환해요.
 * @throws {Error} `VisibilityChangedProvider` 내부에서 사용되지 않았을 때 에러를 발생시켜요.
 * @example
 * ```typescript
 * const isVisible = useVisibilityChanged();
 * console.log(isVisible);
 * // true or false
 * ```
 */
export function useVisibilityChanged(): boolean {
  const isVisible = useContext(VisibilityChangedContext);

  if (isVisible == null) {
    throw new Error('useVisibilityChanged must be used within a VisibilityChangedProvider');
  }

  return isVisible;
}
