import history from 'history/browser';
import UrlPattern from 'url-pattern';

export const LOCATION_CHANGED = 'ROUTER_LOCATION_CHANGED';
export const PUSH = 'ROUTER_PUSH';
export const REPLACE = 'ROUTER_REPLACE';

export function locationChanged(route, params, location) {
  Object.keys(params).forEach(key => {
    params[key] = decodeURIComponent(params[key]);
  });

  const query = {};
  new URLSearchParams(location.search).forEach((value, key) => {
    query[key] = value;
  });

  return {
    type: LOCATION_CHANGED,
    route,
    params,
    query,
    path: decodeURIComponent(location.pathname)
  };
}

export function push(path) {
  return {
    type: PUSH,
    path
  };
}

export function replace(path) {
  return {
    type: REPLACE,
    path
  };
}

export function routeReducer(state = {}, { type, ...action }) {
  if (type === LOCATION_CHANGED) {
    return action;
  }

  return state;
}

export function routeMiddleware() {
  return next => action => {
    switch (action.type) {
      case PUSH:
        history.push(`${action.path}`);
        break;
      case REPLACE:
        history.replace(action.path);
        break;
      default:
        return next(action);
    }
  };
}

function match(routes, location) {
  for (let i = 0; i < routes.length; i++) {
    const params = routes[i].pattern.match(location.pathname);
    if (params !== null) {
      return locationChanged(routes[i].name, params, location);
    }
  }
}

export default function initRouter(routes, store) {
  const patterns = [];
  const opts = {
    segmentValueCharset: 'a-zA-Z0-9-_.%'
  };

  Object.keys(routes).forEach(name =>
    patterns.push({
      name,
      pattern: new UrlPattern(routes[name], opts)
    })
  );

  let matched = match(patterns, history.location);
  if (matched) {
    store.dispatch(matched);
  }

  history.listen(({ location }) => {
    const nextMatch = match(patterns, location);
    if (nextMatch && nextMatch.path !== matched?.path) {
      matched = nextMatch;
      store.dispatch(matched);
    }
  });
}