/*
  Copyright 2018 Google LLC

  Use of this source code is governed by an MIT-style
  license that can be found in the LICENSE file or at
  https://opensource.org/licenses/MIT.
*/

import type { HTTPMethod } from "./constants.js";
import { Route } from "./Route.js";
import type { RouteHandler, RouteMatchCallback, RouteMatchCallbackOptions } from "./types.js";
import { assert } from "./utils/assert.js";
import { logger } from "./utils/logger.js";

/**
 * A class that makes it easy to create a {@linkcode Route} object with a regular expression.
 *
 * For same-origin requests the `RegExp` only needs to match part of the URL. For
 * requests against third-party servers, you must define a `RegExp` that matches
 * the start of the URL.
 */
export class RegExpRoute extends Route {
  /**
   * If the regular expression contains
   * [capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references),
   * the captured values will be passed to the `params` argument.
   *
   * @param regExp The regular expression to match against URLs.
   * @param handler A callback function that returns a `Promise` resulting in a `Response`.
   * @param method The HTTP method to match the {@linkcode Route} against. Defaults to `GET`.
   * against.
   */
  constructor(regExp: RegExp, handler: RouteHandler, method?: HTTPMethod) {
    if (process.env.NODE_ENV !== "production") {
      assert!.isInstance(regExp, RegExp, {
        moduleName: "serwist",
        className: "RegExpRoute",
        funcName: "constructor",
        paramName: "pattern",
      });
    }

    const match: RouteMatchCallback = ({ url }: RouteMatchCallbackOptions) => {
      const result = regExp.exec(url.href);

      // Return immediately if there's no match.
      if (!result) {
        return;
      }

      // Require that the match start at the first character in the URL string
      // if it's a cross-origin request.
      // See https://github.com/GoogleChrome/workbox/issues/281 for the context
      // behind this behavior.
      if (url.origin !== location.origin && result.index !== 0) {
        if (process.env.NODE_ENV !== "production") {
          logger.debug(
            `The regular expression '${regExp.toString()}' only partially matched against the cross-origin URL '${url.toString()}'. RegExpRoute's will only handle cross-origin requests if they match the entire URL.`,
          );
        }

        return;
      }

      // If the route matches, but there aren't any capture groups defined, then
      // this will return [], which is truthy and therefore sufficient to
      // indicate a match.
      // If there are capture groups, then it will return their values.
      return result.slice(1);
    };

    super(match, handler, method);
  }
}
