import parseVersion from 'vparse';
import { presetsCdnUrl, ociCdnUrl, wmfSitematrixCdnUrl } from '../../config/id.js';

import packageJSON from '../../package.json';

let _mainFileFetcher = coreFileFetcher(); // singleton

export { _mainFileFetcher as fileFetcher };

//
// coreFileFetcher asynchronously fetches data from JSON files
//
export function coreFileFetcher() {
  const ociVersion = packageJSON.dependencies['osm-community-index'] || packageJSON.devDependencies['osm-community-index'];
  const v = parseVersion(ociVersion);
  const ociVersionMinor = `${v.major}.${v.minor}`;
  const presetsVersion = packageJSON.devDependencies['@openstreetmap/id-tagging-schema'];

  let _this = {};
  let _inflight = {};
  let _fileMap = {
    'address_formats': 'data/address_formats.min.json',
    'imagery': 'data/imagery.min.json',
    'intro_graph': 'data/intro_graph.min.json',
    'keepRight': 'data/keepRight.min.json',
    'languages': 'data/languages.min.json',
    'locales': 'locales/index.min.json',
    'phone_formats': 'data/phone_formats.min.json',
    'qa_data': 'data/qa_data.min.json',
    'shortcuts': 'data/shortcuts.min.json',
    'territory_languages': 'data/territory_languages.min.json',
    'oci_defaults': ociCdnUrl.replace('{version}', ociVersionMinor) + 'dist/defaults.min.json',
    'oci_features': ociCdnUrl.replace('{version}', ociVersionMinor) + 'dist/featureCollection.min.json',
    'oci_resources': ociCdnUrl.replace('{version}', ociVersionMinor) + 'dist/resources.min.json',
    'presets_package': presetsCdnUrl.replace('{presets_version}', presetsVersion) + 'package.json',
    'deprecated': presetsCdnUrl + 'dist/deprecated.min.json',
    'discarded': presetsCdnUrl + 'dist/discarded.min.json',
    'preset_categories': presetsCdnUrl + 'dist/preset_categories.min.json',
    'preset_defaults': presetsCdnUrl + 'dist/preset_defaults.min.json',
    'preset_fields': presetsCdnUrl + 'dist/fields.min.json',
    'preset_presets': presetsCdnUrl + 'dist/presets.min.json',
    'wmf_sitematrix': wmfSitematrixCdnUrl.replace('{version}', '0.2') + 'data/wikipedia.min.json'
  };

  let _cachedData = {};
  // expose the cache; useful for tests
  _this.cache = () => _cachedData;


  // Returns a Promise to fetch data
  // (resolved with the data if we have it already)
  _this.get = (which) => {
    if (_cachedData[which]) {
      return Promise.resolve(_cachedData[which]);
    }

    const file = _fileMap[which];
    const url = file && _this.asset(file);
    if (!url) {
      return Promise.reject(`Unknown data file for "${which}"`);
    }

    if (url.includes('{presets_version}')) {
      return _this.get('presets_package')
        .then(result => {
          const presetsVersion = result.version;
          return getUrl(url.replace('{presets_version}', presetsVersion), which);
        });
    } else {
      return getUrl(url, which);
    }
  };

  function getUrl(url, which) {
    let prom = _inflight[url];
    if (!prom) {
      _inflight[url] = prom = (window.VITEST ? import(`../${url}`) : fetch(url))
        .then(response => {
          if (window.VITEST) return response.default;

          if (!response.ok || !response.json) {
            throw new Error(response.status + ' ' + response.statusText);
          }
          if (response.status === 204 || response.status === 205) return;  // No Content, Reset Content
          return response.json();
        })
        .then(result => {
          delete _inflight[url];
          if (!result) {
            throw new Error(`No data loaded for "${which}"`);
          }
          _cachedData[which] = result;
          return result;
        })
        .catch(err => {
          delete _inflight[url];
          throw err;
        });
    }

    return prom;
  }


  // Accessor for the file map
  _this.fileMap = function(val) {
    if (!arguments.length) return _fileMap;
    _fileMap = val;
    return _this;
  };

  let _assetPath = '';
  _this.assetPath = function(val) {
    if (!arguments.length) return _assetPath;
    _assetPath = val;
    return _this;
  };

  let _assetMap = {};
  _this.assetMap = function(val) {
    if (!arguments.length) return _assetMap;
    _assetMap = val;
    return _this;
  };

  _this.asset = (val) => {
    if (/^http(s)?:\/\//i.test(val)) return val;
    const filename = _assetPath + val;
    return _assetMap[filename] || filename;
  };

  return _this;
}
