import { Request, Response, NextFunction } from 'express';
import { parse as parseUrl, Url } from 'url';
import { RedirectPath } from './types';
import { url } from './common';

export function redirectHandler(
  fromPath: string,
  toPath: RedirectPath | string,
) {
  const route = url.Route.create(fromPath);

  return (req: Request, res: Response, next: NextFunction) => {
    // Only allow GET requests.
    if (req.method !== 'GET') {
      return next();
    }

    // Match the URL.
    const requestUrl = parseUrl(req.url, true);
    const pathname = requestUrl.pathname;

    if (route.isMatch(pathname)) {
      const params = route.params(pathname);

      // Build the URL.
      const value =
        typeof toPath === 'function'
          ? toPath({ url: requestUrl, params })
          : toPath;

      const url = parseUrl(value, true);
      let path = url.pathname || '';
      const hasQuery = url.search || requestUrl.search;
      if (hasQuery) {
        const query = toMergedQuery(url, requestUrl);
        path = `${path}?${query}`;
      }

      // Redirect to the new URL.
      return res.redirect(path);
    }

    return next();
  };
}

/**
 * INTERNAL
 */
const toMergedQuery = (...urls: Url[]) => {
  const merged = urls.reduce((acc, url) => {
    const query = url.query as object;
    return { ...acc, ...query };
  }, {});

  const toQuery = (key: string, value: string) =>
    value ? `${key}=${value}` : key;

  const query = Object.keys(merged).map(key => toQuery(key, merged[key]));

  return query.join('&');
};
