import routes from 'config/route';
import { config } from 'config/general';

import get from 'lodash/get';
import findKey from 'lodash/findKey';
import findIndex from 'lodash/findIndex';
import find from 'lodash/find';

import qs from 'query-string';

export const getObjectFromLanguage = (obj, value) => {
  const keys = Object.keys(obj);

  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const val = obj[key];
    let found = false;
    config.availableLanguages.forEach(lg => {
      if (val[lg] === value) {
        found = true;
      }
    });

    if (found) {
      return val;
    }
  }
  return false;
};

// Return real url from a path and a language lg
const getCombinedRoute = (all: any, path: string | string[], lg: string): string => {
  const result = [];

  if (path && !Array.isArray(path)) path = path.split('.');

  // first level routes (home / ...)
  if (!Array.isArray(path)) {
    let url = get(all, path);
    if (url) {
      // fallback to default language
      url = url[lg] || url[config.defaultLanguage];
    }
    // fallback to path is not match
    return url || path;
  }

  let currentStructure = all;
  for (let index = 0; index < path.length; index++) {
    const pathElement = path[index];

    currentStructure = get(currentStructure, pathElement);

    if (!currentStructure) {
      // if no match in routes, return joined path
      return path.join('/');
    }

    // fallback to default language for not translated routes
    const urlPart = currentStructure[lg] || currentStructure[config.defaultLanguage];
    if (urlPart) {
      result.push(urlPart);
    }

    if (currentStructure.params) {
      let param = currentStructure.params;
      // regex to remove parameters value that are with brackets (eg : (nl|fr))
      param = param.replace(/\((.*)\)/, '');
      result.push(param);
    }
  }

  return result.join('/');
};

// Create the corresponding route ID from a de-structured paths and the config object (all)
const getRouteInfo = (all: any, splitUrl: string[], lg: string, defaultLanguage?: string): any => {
  let current = all;

  const paths = [];
  const url = [];
  const params = {};
  let routeType = null;

  // Find key for translated value { fr : inscription } => sign-up
  for (let index = 0; index < splitUrl.length; index++) {
    const urlPath = splitUrl[index];

    let key = findKey(current, { [lg]: urlPath });
    // fallback to default language in the urls
    if (!key) key = findKey(current, { [defaultLanguage]: urlPath });

    if (key) {
      current = current[key];
      // result.push(key);

      url.push(key);
      paths.push(key);
      if (current.ROUTE_TYPE) routeType = current.ROUTE_TYPE;
    }
    // If no occurrence found for value, search if params
    if (!key) {
      // search for an object with a params value in it => example type: { params: ':type' } => return {params: ':type'}
      const structureWithParams = find(current, 'params');

      if (structureWithParams) {
        // in the example above id will be "type"
        const id = findKey(current, structureWithParams);
        paths.push(id);

        let param = structureWithParams.params;
        url.push(param);
        param = param.replace(':', '');
        // remove param values that are between ( ). Example: type(type1|type2|...)
        if (param.indexOf('(') > 0) param = param.slice(0, param.indexOf('('));
        params[param] = urlPath;

        current = structureWithParams;
        if (current.ROUTE_TYPE) routeType = current.ROUTE_TYPE;
      }
    }
  }

  return { path: paths.join('.'), url: `/${url.join('/')}`, params, lg, routeType };
};

// eg: <Route path={getUrlFromPath('search', match)} />
export const getUrlFromPath = (path: string | any, match: any): string => {
  const lg = match?.params?.lg || config.defaultLanguage;

  if (!match) {
    console.log('error: no match provided to getUrlFromPath', path);
    return '';
  }

  // Get real url from path and lg
  path = getCombinedRoute(routes, path, lg);

  // if path obtained is a first level route with a parameter (should not happen)
  if (path?.params) {
    path = path.params;
  }

  // default home
  if (match?.path === '/' || (match?.params && !match.params.lg)) {
    return `/${path}`;
  }

  // include lg
  if (match?.params?.lg) {
    // we assume that path includes the language part as a first parameter (eg: /:lg/rest... )
    const splitPath = match.path.split('/'); // is generally equal to :lg
    return `/${splitPath[1]}/${path}`;
  }

  return path;
};

// alias
export const getUrl = getUrlFromPath;

// eg: <NavLink to={pushRoute('home', match)}>
// match can be just a string with wanted lg
// options is for all params eg: pushRoute('home', match, {id: ...})
export const pushRoute = (
  path: string | string[],
  match?: any | string, // or lg
  options?: any, // variables to inject in the path
  search?: any | string | null,
): string => {
  let lg;

  // Case pushRoute(path, 'fr') match is not object but we get language
  if (typeof match === 'string') {
    lg = match;
  } else {
    // Case we give match
    lg = match?.params?.lg || config.defaultLanguage;
  }

  // get real url from path and lg
  path = getCombinedRoute(routes, path, lg);

  // if params are in path ( ex: /..rest/:id )
  if (path?.includes('/:')) {
    if (typeof path === 'string') path = path.split('/');

    for (let index = 0; index < path.length; index++) {
      let partOfPath = path[index];

      if (partOfPath.indexOf(':') < 0) continue;

      partOfPath = partOfPath.replace(':', '');

      const variables = options || match?.params;

      if (variables?.[partOfPath]) {
        path[index] = variables?.[partOfPath];
      }
    }
  }

  // revert path to a string
  if (Array.isArray(path)) {
    path = path.join('/');
  }

  // add optional search to path
  if (search && typeof search === 'string') {
    path += search;
  }

  // Case language in the url and match is not a string
  // If type of match = string ( eg: 'fr' ) params is missing
  if (lg !== config.defaultLanguage && !config.noLanguageInUrl) {
    return `/${lg}/${path}`;
  }
  return `/${path}`;
};

/**
 * From location to route info
 * @location Object : classic location object with pathname
 * @location string : url
 */
export const getRouteInfoFromLocation = (location: any | string): string | object => {
  let splitLocation = [];
  let lg;

  if (typeof location === 'string') splitLocation = location.split('/');

  // split location for get an Array (if location object)
  if (location && location.pathname && typeof location.pathname === 'string') splitLocation = location.pathname.split('/');

  // becasuse first element of splitLocation is all time [''] => remove (because url start with /)
  if (splitLocation) splitLocation.shift();

  // Check if search in location
  const searchIndex = findIndex(splitLocation, el => el.includes('?'));

  let searchParams;
  if (searchIndex !== -1) {
    let splitSearch = splitLocation[searchIndex];
    splitSearch = splitSearch.split('?');
    splitLocation[searchIndex] = splitSearch[0];

    searchParams = qs.parse(splitSearch[1]);
  }

  // check if in splitLocation one of available languages is, if true remove them
  for (let index = 0; index < config.availableLanguages.length; index++) {
    const lang = config.availableLanguages[index];

    for (let j = 0; j < splitLocation.length; j++) {
      const path = splitLocation[j];
      if (path === lang) {
        // we found the language in the url
        lg = path;
        splitLocation.shift();
        break;
      }
    }
  }

  const routeInfo = getRouteInfo(routes, splitLocation, lg, config.defaultLanguage);

  return { ...routeInfo, searchParams };
};
