/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

'use strict';

const nullthrows = require('nullthrows');
const parseCustomTransformOptions = require('./parseCustomTransformOptions');
const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath');
const url = require('url');

const {revisionIdFromString} = require('../IncrementalBundler');

import type {RevisionId} from '../IncrementalBundler';
import type {BundleOptions} from '../shared/types.flow';

function getBoolOptionFromQuery(
  query: {[string]: string},
  opt: string,
  defaultVal: boolean,
): boolean {
  if (query[opt] == null) {
    return defaultVal;
  }

  return query[opt] === 'true' || query[opt] === '1';
}

function parseOptionsFromUrl(
  reqUrl: string,
  platforms: Set<string>,
): {|
  revisionId: ?RevisionId,
  options: BundleOptions,
|} {
  // `true` to parse the query param as an object.
  const urlObj = nullthrows(url.parse(reqUrl, true));
  const urlQuery = nullthrows(urlObj.query);

  const pathname =
    urlObj.pathname != null ? decodeURIComponent(urlObj.pathname) : '';

  let bundleType = 'bundle';

  // Backwards compatibility. Options used to be as added as '.' to the
  // entry module name. We can safely remove these options.
  const entryFileRelativeToProjectRoot = pathname
    .replace(/^(?:\.?\/)?/, './') // We want to produce a relative path to project root
    .split('.')
    .filter((part, i) => {
      if (part === 'delta' || part === 'map' || part === 'meta') {
        bundleType = part;
        return false;
      }
      if (
        part === 'includeRequire' ||
        part === 'runModule' ||
        part === 'bundle' ||
        part === 'assets'
      ) {
        return false;
      }
      return true;
    })
    .join('.');

  // try to get the platform from the url
  const platform =
    urlQuery.platform || parsePlatformFilePath(pathname, platforms).platform;

  const revisionId = urlQuery.revisionId || urlQuery.deltaBundleId || null;

  const dev = getBoolOptionFromQuery(urlQuery, 'dev', true);
  const minify = getBoolOptionFromQuery(urlQuery, 'minify', false);
  const excludeSource = getBoolOptionFromQuery(
    urlQuery,
    'excludeSource',
    false,
  );
  const inlineSourceMap = getBoolOptionFromQuery(
    urlQuery,
    'inlineSourceMap',
    false,
  );
  const runModule = getBoolOptionFromQuery(urlQuery, 'runModule', true);

  const customTransformOptions = parseCustomTransformOptions(urlObj);

  return {
    revisionId: revisionId != null ? revisionIdFromString(revisionId) : null,
    options: {
      customTransformOptions,
      dev,
      hot: true,
      minify,
      platform,
      onProgress: null,
      entryFile: entryFileRelativeToProjectRoot,
      bundleType,
      sourceMapUrl: url.format({
        ...urlObj,
        // The remote chrome debugger loads bundles via Blob urls, whose
        // protocol is blob:http. This breaks loading source maps through
        // protocol-relative URLs, which is why we must force the HTTP protocol
        // when loading the bundle for either iOS or Android.
        protocol:
          platform != null && platform.match(/^(android|ios)$/) ? 'http' : '',
        pathname: pathname.replace(/\.(bundle|delta)$/, '.map'),
      }),
      runModule,
      excludeSource,
      inlineSourceMap,
    },
  };
}

module.exports = parseOptionsFromUrl;