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) {
  return {
    type: LOCATION_CHANGED,
    route,
    params,
    location
  };
}

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

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

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

  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 decode(location) {
  location.pathname = decodeURIComponent(location.pathname);
  return location;
}

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

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);
  } else {
    matched = { location: {} };
  }

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