import { NativeModules } from "react-native";
import * as R from "ramda";
import md5 from "md5";

import { reducePromises } from "@applicaster/zapp-react-native-utils/arrayUtils";
import { bridgeLogger } from "../logger";

const { OfflineAssetsBridge } = NativeModules as {
  OfflineAssetsBridge: OfflineAssetsBridgeI;
};

const defaultOptions = { abortOnFail: false };

export function isAssetCacheEnabled(): boolean {
  return typeof OfflineAssetsBridge !== "undefined";
}

const reduceArrayResponse = R.when(
  Array.isArray,
  R.reduce((results, fileResult) => {
    return R.mergeLeft(
      results,
      R.zipObj(R.keys(fileResult), R.values(fileResult))
    );
  }, {})
);

export async function storeFiles(
  files: FilesList,
  options: StoreFileOptions = defaultOptions
): Promise<StoredFilesResult> {
  const result = await OfflineAssetsBridge?.storeFiles?.(files, options);

  return reduceArrayResponse(result);
}

export function deleteFolders(folders: string[]): Promise<boolean> {
  return reducePromises<string>(
    (current: string, _) => OfflineAssetsBridge?.delete?.(current),
    Promise.resolve(),
    folders
  );
}

export function getNativeRootPath(): Promise<string> {
  return OfflineAssetsBridge?.getFilesDirectory?.();
}

function isImageAsset(value) {
  if (typeof value !== "string") {
    return false;
  }

  return /\.(jpg|png|jpeg)$/.test(value.split("?")[0]);
}

function getNativeFileName(nativeRootPath, url, path) {
  const fileNameWithoutQuery = url.split("?")[0];
  const extension = R.compose(R.last, R.split("."))(fileNameWithoutQuery);

  return `${nativeRootPath}/${path}/${md5(url)}.${extension}`;
}

function propOrIndex(value) {
  const number = Number(value);

  return Number.isNaN(number) ? value : number;
}

export const fileIsSaved = R.curry((savedFilesResult, { file }) => {
  if (typeof savedFilesResult === "undefined") return true;

  return (
    savedFilesResult?.[file] ||
    savedFilesResult?.[file?.replace("file://", "")] ||
    savedFilesResult?.[`file://${file}`]
  );
});

export const remapAssetPath = (
  assets: FilesList,
  source: Record<string, any>,
  localFiles: Record<string, string>,
  tag: string = "unknown"
): Record<string, any> => {
  const startTime = performance.now();

  const result = assets.reduce(
    (obj, { path, url }) => {
      const localFile = localFiles[url];

      if (!localFile) {
        bridgeLogger.warning({
          message: `remapAssetPath: Could not replace asset with local file for url: ${url}`,
          data: { path, obj },
        });

        return obj;
      }

      let target = obj;

      // Traverse the path to the appropriate nested object
      for (let i = 0; i < path.length - 1; i++) {
        target = target[path[i]] = target[path[i]];
      }

      if (!target) {
        bridgeLogger.error({
          message: `remapAssetPath: Could not find path ${path.join(".")}`,
          data: obj,
        });
      } else {
        // Set the final value
        target[path[path.length - 1]] = localFile;
      }

      return obj;
    },
    JSON.parse(JSON.stringify(source))
  );

  bridgeLogger.log({
    message: `remapAssetPath: remapping took ${
      performance.now() - startTime
    } ms, tag: ${tag}`,
  });

  return result;
};

export function collectAssets(object, nativeRootPath, source, path = []) {
  if (!object) {
    const message = `collectAssets: object is null or undefined for source: ${source}`;
    bridgeLogger.error({ message, data: { object, path } });
    throw new Error(message);
  }

  return Object.entries(object).reduce((assets, [prop, value]) => {
    const propPath = [...path, propOrIndex(prop)];

    if (R.is(Object, value) && !R.isEmpty(value)) {
      assets.push(...collectAssets(value, nativeRootPath, source, propPath));
    } else if (isImageAsset(value)) {
      assets.push({
        url: value,
        path: propPath,
        file: getNativeFileName(nativeRootPath, value, source),
        source,
      });
    }

    return assets;
  }, []);
}

export const getFolders = R.compose(
  R.uniq,
  // @ts-ignore
  R.map(R.compose(R.join("/"), R.init, R.split("/"), R.prop("file")))
);

if (process.env.NODE_ENV === "test") {
  module.exports = {
    isImageAsset,
    getNativeFileName,
    propOrIndex,
    remapAssetPath,
    collectAssets,
    getFolders,
    deleteFolders,
    getNativeRootPath,
    storeFiles,
    reduceArrayResponse,
    fileIsSaved,
  };
}
