text: // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  // See LICENSE.txt for license information.
  
  // Based on the work done by https://github.com/wcandillon/react-native-expo-image-cache/
  
  import {Platform} from 'react-native';
  import RNFetchBlob from 'rn-fetch-blob';
  
  import Url from 'url-parse';
  
  import {Client4} from '@client/rest';
  
  import {DeviceTypes} from 'app/constants';
  import {
      getExtensionFromMime,
      getExtensionFromContentDisposition,
      hashCode,
  } from 'app/utils/file';
  import mattermostBucket from 'app/mattermost_bucket';
  
  const {IMAGES_PATH} = DeviceTypes;
  const DEFAULT_MIME_TYPE = 'image/png';
  let siteUrl;
  
  export default class ImageCacheManager {
      static listeners = {};
  
      static cache = async (filename, fileUri, listener) => {
          if (!listener) {
              console.warn('Unable to cache image when no listener is provided'); // eslint-disable-line no-console
          }
  
          const uri = parseUri(fileUri);
          const {path, exists} = await getCacheFile(filename, uri);
          const prefix = Platform.OS === 'android' ? 'file://' : '';
          let pathWithPrefix = `${prefix}${path}`;
  
          if (exports.isDownloading(uri)) {
              addListener(uri, listener);
          } else if (exists) {
              listener(pathWithPrefix);
          } else {
              addListener(uri, listener);
              if (uri.startsWith('http')) {
                  try {
                      const certificate = await mattermostBucket.getPreference('cert');
                      const options = {
                          session: uri,
                          timeout: 10000,
                          indicator: true,
                          overwrite: true,
                          path,
                          certificate,
                      };
  
                      const headers = {};
                      if (uri.includes(Client4.getUrl()) || uri.includes(siteUrl)) {
                          headers.Authorization = `Bearer ${Client4.getToken()}`;
                          headers['X-Requested-With'] = 'XMLHttpRequest';
                      }
  
                      this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri, headers);
                      if (this.downloadTask.respInfo.respType === 'text') {
                          throw new Error();
                      }
  
                      const contentDisposition = this.downloadTask.respInfo.headers['Content-Disposition'];
                      const mimeType = this.downloadTask.respInfo.headers['Content-Type'];
                      const ext = `.${
                          getExtensionFromContentDisposition(contentDisposition) ||
                          getExtensionFromMime(mimeType) ||
                          getExtensionFromMime(DEFAULT_MIME_TYPE)
                      }`;
  
                      if (!path.endsWith(ext)) {
                          const oldExt = path.substring(path.lastIndexOf('.'));
                          const newPath = path.replace(oldExt, ext);
                          await RNFetchBlob.fs.mv(path, newPath);
  
                          pathWithPrefix = `${prefix}${newPath}`;
                      }
  
                      notifyAll(uri, pathWithPrefix);
                  } catch (e) {
                      RNFetchBlob.fs.unlink(pathWithPrefix);
                      notifyAll(uri, null);
                      unsubscribe(uri);
                      return null;
                  }
              } else {
                  // In case the uri we are trying to cache is already a local file just notify and return
                  notifyAll(uri, uri);
              }
  
              unsubscribe(uri);
          }
  
          return pathWithPrefix;
      };
  }
  
  const parseUri = (uri) => {
      const url = new Url(uri);
      return url.href;
  };
  
  export const getCacheFile = async (name, uri) => {
      const filename = name || uri.substring(uri.lastIndexOf('/'), uri.indexOf('?') === -1 ? uri.length : uri.indexOf('?'));
      const defaultExt = `.${getExtensionFromMime(DEFAULT_MIME_TYPE)}`;
      const ext = filename.indexOf('.') === -1 ? defaultExt : filename.substring(filename.lastIndexOf('.'));
  
      let path = `${IMAGES_PATH}/${Math.abs(hashCode(uri))}${ext}`;
  
      try {
          const isDir = await RNFetchBlob.fs.isDir(IMAGES_PATH);
          if (!isDir) {
              await RNFetchBlob.fs.mkdir(IMAGES_PATH);
          }
      } catch (error) {
          // do nothing
      }
  
      let exists = await RNFetchBlob.fs.exists(path);
      if (!exists) {
          const pathWithDiffExt = await RNFetchBlob.fs.existsWithDiffExt(path);
          if (pathWithDiffExt) {
              exists = true;
              path = pathWithDiffExt;
          }
      }
  
      return {exists, path};
  };
  
  export const getSiteUrl = () => {
      return siteUrl;
  };
  
  export const setSiteUrl = (url) => {
      siteUrl = url;
  };
  
  export const isDownloading = (uri) => Boolean(ImageCacheManager.listeners[uri]);
  
  const addListener = (uri, listener) => {
      if (!ImageCacheManager.listeners[uri]) {
          ImageCacheManager.listeners[uri] = [];
      }
      ImageCacheManager.listeners[uri].push(listener);
  };
  
  const unsubscribe = (uri) => Reflect.deleteProperty(ImageCacheManager.listeners, uri);
  
  const notifyAll = (uri, path) => {
      ImageCacheManager.listeners[uri].forEach((listener) => {
          if (typeof listener === 'function') {
              listener(path);
          }
      });
  };
  