import { loadScript } from "@braintree/asset-loader";
import { BtModuleKeys, BtModuleLoadConfig } from "./types";
import { isAmdEnv } from "../amd-utils";

/**
 * Safely loads BT modules by checking if the module already exists and verifying if versions mismatch
 *
 * @param loadConfig <BtModuleLoadConfig> Configuration of BT Module to load
 * @param version <string> version that should be passed from the client getVersion
 * @returns Promise<HTMLScriptElement>
 * @returns Promise<true> when BT module with same version already exists
 * @returns Promise.reject(err) when BT module already exists but versions mismatch or empty version passed in
 */
export async function safeLoadBtModule(
  loadConfig: BtModuleLoadConfig,
  version: string,
  minified = true
) {
  const bt = getBraintree();
  if (bt && bt[loadConfig.module]) {
    if (version && bt[loadConfig.module]?.VERSION !== version) {
      const existingVersion = bt[loadConfig.module]?.VERSION;

      throw new Error(
        `${loadConfig.module} already loaded with version ${existingVersion} cannot load version ${version}`
      );
    } else {
      return true;
    }
  }

  if (!version) {
    throw new Error(
      `Attempted to load ${loadConfig.module} without specifying version`
    );
  }

  return loadBtModule(loadConfig, version, minified);
}

/**
 * Reads the version and to load the correct version of Bt module
 *
 * @param loadConfig <BtModuleLoadConfig> Configuration of BT Module to load
 * @param version <string> Bt module version
 * @returns Promise<HTMLScriptElement> or
 */
function loadBtModule(
  loadConfig: BtModuleLoadConfig,
  version: string,
  minified = true
) {
  if (isAmdEnv()) {
    const module = minified
      ? loadConfig.amdModule.minified
      : loadConfig.amdModule.unminified;
    return new Promise<void>((resolve, reject) => {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      window.require([module], resolve, reject);
    });
  }
  const script = minified
    ? loadConfig.script.minified
    : loadConfig.script.unminified;
  return loadScript({
    id: `${loadConfig.id}-${version}`,
    src: `https://js.braintreegateway.com/web/${version}/js/${script}`,
  });
}

/**
 * Looks for the Braintree web sdk on the window object
 *
 * @returns Braintree web sdk
 */
function getBraintree() {
  return window?.braintree;
}

declare global {
  interface Window {
    braintree: Partial<
      Record<BtModuleKeys, { create: () => void; VERSION: string }>
    > & {
      fastlane?: object;
    };
  }
}
