import * as React from "react";
import * as R from "ramda";
import {
  initialWindowMetrics,
  SafeAreaProvider,
} from "react-native-safe-area-context";

import { ErrorBoundary } from "./ErrorBoundary";
import { RouteManager } from "./RouteManager";
import { withNavigationProvider } from "./NavigationProvider";
import { withNetworkStatusProvider } from "./NetworkStatusProvider";
import { withActionsProvider } from "./ActionsProvider";
import { storeConnector } from "./StoreConnector";
import { appLifeCycleManager } from "./appLifeCycleManager";
import { URLSchemeListener } from "./DeepLinking/URLSchemeListener";
import { withAppManager } from "./AppStateDecorator";
import { ThemeManager } from "./ThemeManager";
import { ModalProvider } from "./ModalProvider";
import { withAnalyticsProvider } from "./AnalyticsProvider";
import { Loader } from "./NavigationProvider/Loader";
import { ApplyScreenHooks } from "./NavigationProvider/ScreenHooks/ApplyScreenHooks";
import {
  ZappPipesEntryContext,
  ZappPipesSearchContext,
} from "@applicaster/zapp-react-native-ui-components/Contexts";

import { withURLSchemeContextProvider } from "./DeepLinking/URLSchemeListener/URLSchemeContextProvider";
import { appRemoteDataLoader } from "./appRemoteDataLoader";
import { withActionExecutor } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
import { withScreenDataContextProvider } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
import { LogicAggregatorContainer } from "./LogicAggregatorContainer";
import { LoadingOverlay } from "./LoadingOverlay";

interface AppOptions {
  Layout: React.ComponentType;
  OfflineHandler: React.ComponentType;
  SplashLoader: React.ComponentType;
  InteractionManager: React.ComponentType;
}

/**
 * Creates a QuickBrick app with the provided options
 * This function is provided as a utility to increase reusability of code across platforms
 * @param {AppOptions} options
 * @returns {ReactComponent}
 */
const App = ({
  Layout,
  OfflineHandler,
  SplashLoader,
  InteractionManager,
}: AppOptions): ZappApp =>
  React.memo(function ZappApp(props: AppComponentProps) {
    const {
      styles,
      components: { AppContainer },
      plugins,
    } = props;

    return (
      <ThemeManager plugins={plugins}>
        <SafeAreaProvider initialMetrics={initialWindowMetrics}>
          <AppContainer styles={styles}>
            <ErrorBoundary>
              <URLSchemeListener>
                <SplashLoader>
                  <OfflineHandler>
                    <Layout>
                      <RouteManager />
                    </Layout>
                  </OfflineHandler>
                </SplashLoader>
                <ModalProvider />
              </URLSchemeListener>
              <LogicAggregatorContainer
                components={[InteractionManager, ApplyScreenHooks]}
              />
              <LoadingOverlay />
            </ErrorBoundary>
          </AppContainer>
        </SafeAreaProvider>
      </ThemeManager>
    );
  });

export function createQuickBrickApp(options): ZappApp {
  const {
    InteractionManager = () => null,
    Layout,
    QuickBrickManager,
    OfflineHandler = R.prop("children"),
    ContextProviders = [R.identity],
    appLoader = () => Promise.resolve(),
    SplashLoader = Loader,
  } = options;

  ContextProviders.push(withAnalyticsProvider);

  // Beware of the order of decorators !
  // It's awesome but it can break everything

  return R.compose(
    storeConnector, // storeConnector should be first, as it disregards other props
    ...ContextProviders, // contexts consumers are only working below this point
    appRemoteDataLoader,
    withAppManager, // provides handlers when the app is going in the background, or comes out of it, can be moved to LogicAggregatorContainer
    ZappPipesEntryContext.withProvider,
    withURLSchemeContextProvider,
    withNetworkStatusProvider,
    withNavigationProvider,
    withScreenDataContextProvider,
    ZappPipesSearchContext.withProvider,
    withActionExecutor,
    withActionsProvider,
    appLifeCycleManager(appLoader, QuickBrickManager) // can be moved to LogicAggregatorContainer
  )(App({ Layout, OfflineHandler, SplashLoader, InteractionManager }));
}
