/**
 * quick-brick-core module
 * @module @applicaster/quick-brick-core/createZappApp
 * This module creates a ZappApp
 */

import * as React from "react";
import * as R from "ramda";
import { Dimensions } from "react-native";
import {
  isNotEmpty,
  isNotNil,
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";

import {
  createZappReduxStore,
  loadAppContextData,
} from "@applicaster/zapp-react-native-redux";

import { coreLogger } from "@applicaster/zapp-react-native-utils/logger";

import injectCorePlugins from "@applicaster/quick-brick-core-plugins";

import { Provider } from "react-redux";

import { EmptyZappApp } from "../defaults";
import { mergeUiComponentPlugins } from "../helpers";
import { isTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
import { actions as appDataActions } from "@applicaster/zapp-react-native-redux/AppData";
import { remoteDataLoader } from "../App/appRemoteDataLoader/remoteDataLoader";
import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";

const isNotEmptyObject = (value: unknown) =>
  isNotNil(value) && R.is(Object, value) && isNotEmpty(value);

/**
 * Creates a Zapp App with the provided options
 * @export
 * @param ZappAppConfig configuration object for the zapp app
 * @returns {React.ComponentType<any>}
 */
export function createZappApp(zappAppConfig: ZappAppConfig) {
  const {
    components = {},
    cellStyles = {},
    tabletCellStyles = null,
    plugins = [],
    pipesEndpoints = {},
    pluginConfigurations = [],
    remoteConfigurations = {},
    layout = {},
    tabletLayout = {},
    reduxStoreOptions = {},
    appSettings = {},
    ZappApp = EmptyZappApp,
    presetsMapping = {},
    tabletPresetsMapping = {},
  } = zappAppConfig;

  const { width, height } = Dimensions.get("screen");

  const useTabletLayout =
    isTablet({ width, height }, width > height ? "landscape" : "portrait") &&
    isNotEmptyObject(tabletLayout) &&
    isNotEmptyObject(tabletCellStyles);

  function fetchPluginConfiguration(plugin) {
    const configuration = R.compose(
      R.unless(
        R.isNil,
        R.compose(
          R.tryCatch(JSON.parse, R.flip(R.identity)),
          R.prop("configuration_json")
        )
      ),
      R.defaultTo(null),
      R.find(R.pathEq(["plugin", "identifier"], plugin.identifier))
    )(pluginConfigurations);

    return {
      ...plugin,
      configuration,
    };
  }

  const pluginsWithConfigurations = R.compose(
    R.map(fetchPluginConfiguration),
    injectCorePlugins
  )(plugins);

  const store = createZappReduxStore(reduxStoreOptions);

  const appComponents = mergeUiComponentPlugins(components, plugins);

  const appProperties = {
    styles: {},
    components: appComponents,
    cellStyles: useTabletLayout ? tabletCellStyles : cellStyles,
    plugins: pluginsWithConfigurations,
    rivers: useTabletLayout ? tabletLayout : layout,
    contentTypes: useTabletLayout
      ? tabletLayout?.content_types
      : layout?.content_types,
    pipesEndpoints,
    pluginConfigurations,
    remoteConfigurations,
    appSettings,
    presetsMapping: useTabletLayout ? tabletPresetsMapping : presetsMapping,
  };

  if (__DEV__) {
    // Handle case when layout is backed in as part of the environment, dev only
    // TODO: Handle it inside getNativeRemoteContextData
    store.dispatch(
      appDataActions.merge({
        layoutVersion: layout?.api_version ? layout.api_version : "v1",
      })
    );
  }

  coreLogger.log({ message: "Initializing Quick Brick App", jsOnly: true });

  loadAppContextData(store.dispatch, appProperties);

  if (isAndroidPlatform()) {
    remoteDataLoader().then(() => {
      coreLogger.log({
        message:
          "createZappApp: Android platform detected. Sending quickBrickReady event",
      });

      // TODO load data here before calling event below
      QuickBrickManager.sendQuickBrickEvent("quickBrickReady");
    });
  }

  const App = (props: AppComponentProps) => (
    <Provider store={store}>
      <ZappApp {...props} />
    </Provider>
  );

  App.displayName = "ZappAppRoot";

  return App;
}
