import { SafeAreaProvider } from '@react-native-bedrock/native/react-native-safe-area-context';
import { ComponentType, PropsWithChildren } from 'react';
import { App } from './App';
import { registerPage } from './registerPage';
import type { InitialProps } from '../initial-props';
import { Router, type RouterProps, type RequireContext } from '../router';
import { BackEventProvider, useBackEventState } from '../use-back-event';

export interface BedrockProps {
  /**
   * @description
   * 앱의 이름예요.
   */
  appName: string;
  /**
   * @description
   * 앱의 컨텍스트예요.
   *
   * @TODO context 숨기기
   */
  context: RequireContext;
  /**
   * @description
   * Bedrock 라우터에 전달할 구성 객체예요.
   */
  router?: RouterProps;
}

/**
 * @internal
 */
interface BedrockAppRootProps extends BedrockProps {
  container: ComponentType<PropsWithChildren<InitialProps>>;
  initialProps: InitialProps;
}

function AppRoot({ appName, context, container, initialProps, router }: BedrockAppRootProps) {
  const backEventState = useBackEventState();

  return (
    <App {...initialProps}>
      <SafeAreaProvider>
        <BackEventProvider backEvent={backEventState}>
          <Router
            context={context}
            initialProps={initialProps}
            container={container}
            canGoBack={!backEventState.hasBackEvent}
            onBack={backEventState.onBack}
            // @TODO intoss 고정 대신 scheme 구성으로 받아서 처리
            prefix={`intoss://${appName}`}
            {...router}
          />
        </BackEventProvider>
      </SafeAreaProvider>
    </App>
  );
}

const createBedrock = () => {
  let _appName: string | null = null;

  return {
    registerApp(
      AppContainer: ComponentType<PropsWithChildren<InitialProps>>,
      { appName, context, router }: BedrockProps
    ): (initialProps: InitialProps) => JSX.Element {
      function Root(initialProps: InitialProps) {
        return (
          <AppRoot
            container={AppContainer}
            initialProps={initialProps}
            appName={appName}
            context={context}
            router={router}
          />
        );
      }

      registerPage(Root);
      _appName = appName;
      return Root;
    },

    get appName(): string {
      if (_appName === null) {
        throw new Error('Bedrock.appName can only be used after registerApp has been called.');
      }
      return _appName;
    },
  };
};

/**
 * @public
 * @category 코어
 * @name Bedrock
 * @description
 *
 * @property {RegisterService} registerApp - 이 함수는 서비스의 기본 환경을 설정해주고, 복잡한 설정을 따로 할 필요 없이 서비스 개발을 빠르게 시작할 수 있게 도와줘요. `appName`만 전달하면 파일 기반 라우팅, 쿼리 파라미터 처리, 뒤로 가기 제어 등 여러 기능을 바로 사용할 수 있어요.
 *
 * `Bedrock.registerApp` 함수가 제공하는 기능은 다음과 같아요.
 * - 라우팅: 파일 경로에 맞게 URL이 자동으로 매핑돼요. Next.js의 파일 기반 라우팅과 비슷한 방식으로 동작해요. 예를 들어 `/my-service/pages/index.ts` 파일은 `intoss://my-service` 주소로 접근할 수 있고, `/my-service/pages/home.ts 파일은 intoss://my-service/home` 주소로 접근할 수 있어요.
 * - 쿼리 파라미터: URL 스킴으로 전달 받은 쿼리 파라미터를 쉽게 사용할 수 있어요. 예를 들어, `referrer` 파라미터를 받아서 로그를 남길 수 있어요.
 * - 뒤로 가기 제어: 뒤로 가기 이벤트를 제어할 수 있어요. 예를 들어, 사용자가 화면에서 뒤로 가기를 누르면 다이얼로그를 띄우거나 화면을 닫을 수 있어요.
 * - 화면 가시성(Visibility): 화면이 사용자에게 보이는지, 가려져 있는지 알 수 있어요. 예를 들어, 사용자가 홈 화면으로 나갔을 때 이 값을 활용해 특정 동작을 처리할 수 있어요.
 *
 * @example
 *
 * ### `Bedrock` 컴포넌트로 만드는 예제
 *
 * ```tsx
 * import { PropsWithChildren } from 'react';
 * import { Bedrock, InitialProps } from 'react-native-bedrock';
 * import { context } from '../require.context';
 *
 * function AppContainer({ children }: PropsWithChildren<InitialProps>) {
 *  return <>{children}</>;
 * }
 *
 * export default Bedrock.registerApp(AppContainer, {
 *  appName: 'my-app',
 *  context,
 * });
 *
 * ```
 */
export const Bedrock = createBedrock();
