import { createZappPipesLibrary } from "@applicaster/zapp-pipes-dev-kit/lib/app-pipes";
import {
  platformSelect,
  getPlatform,
} from "@applicaster/zapp-react-native-utils/reactUtils";
import * as R from "ramda";
import { getAppData } from "../QuickBrick";
import { localStorage } from "../ZappStorage/LocalStorage";
import { sessionStorage } from "../ZappStorage/SessionStorage";
import type { AppData } from "../QuickBrick";

type Provider = {
  name: string;
  handler: (any) => (any) => Promise<any>;
  manifest: {
    handlers: Array<string>;
    help: any;
  };
  test: {
    requestMocks: any;
    testCommand: string;
  };
};

type ZappPipesGetter = {
  get: (url: string, callback: (any) => void) => void;
};

let cachedProviders: Array<Provider> | null = [];
let cachedZappPipesAdapter: ZappPipesGetter | null;

/**
 * this functions add properties which may be missing from the native bridge
 * This is required mainly for applicaster2 provider signed requests
 */
function getPlatformProperties() {
  return platformSelect({
    default: { platform: getPlatform() },
    android_tv: {
      os_type: "android",
      os_version: 28,
      platform: "android",
    },
    android: {
      os_type: "android",
      os_version: 28,
      platform: "android",
    },
    tvos: { os_type: "ios", platform: "ios" },
    ios: { os_type: "ios", platform: "ios" },
  });
}

/**
 * Creates a Zapp pipes adapter if necessary
 * will only invoke the createZappPipesLibrary function if the adapter is not cached
 * or if providers array has changed
 *
 * @export
 * @param {Array<Provider>} providers to inject in the adapter
 * @returns {ZappPipesGetter} Object containing a get method to load zapp pipes data source urls
 */
export function bootstrapZappPipes(
  providers: Array<Provider>
): ZappPipesGetter {
  if (!cachedZappPipesAdapter || !R.equals(providers, cachedProviders)) {
    const zappPipesAdapter: ZappPipesGetter = {
      get: createZappPipesLibrary({
        providers,
        release: "dev",
        nativeBridge: {
          appData(): AppData {
            const appData = getAppData();

            const additionalPlatformProperties = getPlatformProperties();

            return { ...appData, ...additionalPlatformProperties };
          },
          getSessionStoreItem: sessionStorage.getItem,
          setSessionStoreItem: sessionStorage.setItem,
          getAllSessionItems: sessionStorage.getAllItems,
          getLocalStoreItem: localStorage.getItem,
          setLocalStoreItem: localStorage.setItem,
        },
      }),
    };

    cachedProviders = providers;
    cachedZappPipesAdapter = zappPipesAdapter;
  }

  return cachedZappPipesAdapter;
}

/**
 * Resets the providers & zapp pipes adapter cache
 *
 * @export
 */
export function resetZappPipesAdapterCache() {
  cachedProviders = null;
  cachedZappPipesAdapter = null;
}

/**
 * Sets the proviers & zapp pipes adapter cache
 * this function is meant to be used only in tests
 *
 * @export
 * @param {Array<Provider>} _cachedProviders : array of providers to set in cache
 * @param {ZappPipesGetter} _cachedZappPipesAdapter : zapp pides adapter to set in cache
 */
export function setZappPipesAdapterCache(
  _cachedProviders: Array<Provider>,
  _cachedZappPipesAdapter: ZappPipesGetter
) {
  if (process && process.env && process.env.NODE_ENV === "test") {
    cachedProviders = _cachedProviders;
    cachedZappPipesAdapter = _cachedZappPipesAdapter;
  }
}
