import { loadScript } from "@braintree/asset-loader";
import { constants, FL_NAMESPACE } from "./constants";
import {
  LoadAxoOptions,
  GenerateAssetUrl,
  AxoSupportedPlatforms,
} from "./types";
import { safeLoadBtModule } from "./lib/safeLoadBtModule";
import { btModulesLoadConfig } from "./lib/safeLoadBtModule/types";
import { isAmdEnv, isRequireJsEnv } from "./lib/amd-utils";

/**
 * Loads accelerated checkout components.
 * @param options object with a minified parameter to determine if the script that is loaded should be minified or not (defaults to true if)
 * @returns an object with metadata with a localeUrl parameter to be read by AXO SDK
 */
async function loadAxo(options: LoadAxoOptions) {
  performance.mark("pp_axo_sdk_init_invoked");
  const { btSdkVersion, minified } = options;
  const assetUrl = getAssetsUrl(options);
  const localeUrl = getLocaleUrl(options);

  if (options.platform === AxoSupportedPlatforms.BT) {
    await Promise.all([
      safeLoadBtModule(
        btModulesLoadConfig.hostedFields,
        btSdkVersion,
        minified
      ),
      loadAXOScript(assetUrl, minified),
    ]);
  } else if (options.platform === AxoSupportedPlatforms.PPCP) {
    await Promise.all([
      safeLoadBtModule(btModulesLoadConfig.client, btSdkVersion, minified),
      safeLoadBtModule(
        btModulesLoadConfig.hostedFields,
        btSdkVersion,
        minified
      ),
      loadAXOScript(assetUrl, minified),
    ]);
  } else {
    throw new Error("unsupported axo platform");
  }

  return { metadata: { localeUrl } };
}

/**
 * Reads the url and to load the axo bundle script
 * @param url (Required) string url for the correct axo asset
 * @returns Promise<HTMLScriptElement>
 */
function loadAXOScript(url: string, minified = true) {
  if (isAmdEnv()) {
    // AMD environment
    if (isRequireJsEnv()) {
      // Let's configure RequireJS
      requirejs.config({
        paths: {
          [FL_NAMESPACE]: url,
        },
      });
    }

    const moduleName = `${FL_NAMESPACE}/${
      minified
        ? constants.AXO_ASSET_NAME.minified
        : constants.AXO_ASSET_NAME.unminified
    }`;

    return new Promise<void>((resolve, reject) => {
      window.require([moduleName], resolve, reject);
    });
  }

  // Not an AMD environment
  return loadScript({
    id: "axo-id",
    src: url,
    forceScriptReload: true,
  });
}

/**
 * Prepends the domain to the asset url
 * @param options object with assetUrl and bundleid parameters to determine which URL to return
 * @returns full domain and assets URL as string
 */
function generateAssetUrl({ assetUrl, bundleId }: GenerateAssetUrl): string {
  return bundleId
    ? `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${assetUrl}`
    : `${constants.CDNX_PROD}/${assetUrl}`;
}

/**
 * Retrieves either the minified or unminified assets URL as specified
 * @param options (Optional) object with a minified and metadata with bundleIdOverride parameters to determine which URL to return
 * @returns assets URL as string
 */
function getAssetsUrl(options?: LoadAxoOptions): string {
  const assetName =
    options?.minified !== false
      ? constants.AXO_ASSET_NAME.minified
      : constants.AXO_ASSET_NAME.unminified;

  const assetUrl = isAmdEnv()
    ? constants.AXO_ASSET_PATH
    : `${constants.AXO_ASSET_PATH}/${assetName}.js`;
  return generateAssetUrl({
    assetUrl,
    bundleId: options?.metadata?.bundleIdOverride,
  });
}

/**
 * Retrieves the Locales URL, the path to our language files
 * @param options (Optional) object with a minified and metadata with bundleIdOverride parameters to determine which URL to return
 * @returns locale URL as string
 */
function getLocaleUrl(options?: LoadAxoOptions): string {
  return generateAssetUrl({
    assetUrl: constants.LOCALE_PATH,
    bundleId: options?.metadata?.bundleIdOverride,
  });
}

export default loadAxo;
