import isString from 'lodash/isString';
import Vue from 'vue';
import Component from 'vue-class-component';
import type { Route } from 'vue-router/types';

type RouteLikeOrPath =  string | { path?: string; name?: string };

/**
 * Добавление методов:
 *
 * returnToParentViews(view: string | { path?: string; name?: string }, ...[view: string | { path?: string; name?: string }]): void
 *
 * returnToParentView(view: string | { path?: string; name?: string }): void
 *
 * returnToParentViewByPath(path: string): void
 *
 * returnToParentViewByName(name: string): void
 * 
 * getParentRoute(parentRotes: Array<string | { path?: string; name?: string }>, default: string | { path?: string; name?: string }): string | { path?: string; name?: string }
 *
 * Метод делает возврат к указанному роуту, если таковой имеется в истории (для сохранения query)
 * или переходит по указанному роуту, если в истории подобной записи нет.
 *
 * Для работы требуется плагин router-history
 */
@Component
export default class ReturnToParentView extends Vue {
  /**
   * Возвращает к одному из перечисленных путей, по возможности сохраняя фильтры в url.
   * Для этого ищет совпадение одного из них в истории с конца
   * @param views - route-like объекты или пути (пути нормализируются)
   */
  returnToParentViews(...views: RouteLikeOrPath[]): void {
    this.returnToParentView(this.getParentRoute(views));
  }

  /**
   * Вернуть к предыдущему виду, по возможности сохраняя фильтры в url
   */
  returnToParentView(view: RouteLikeOrPath): void {
    /* Если пришла строка, считать ее path */
    if (typeof view === 'string') {
      return this.returnToParentViewByPath(view);
    }

    /* Если пришел объект */
    if (typeof view === 'object') {
      /* Если в объекте есть поле name, использовать его */
      if (view.name) {
        return this.returnToParentViewByName(view.name);
      }

      /* Если в объекте есть поле path, использовать его */
      if (view.path) {
        return this.returnToParentViewByPath(view.path);
      }
    }

    /* Если ничего из указанного, то просто вернуться на шаг назад */
    return this.$router.back();
  }

  /**
   * Вернуться к предыдущему логическому виду(по пути)
   */
  returnToParentViewByPath(path: string): void {
    const index = this.$router.$history.findIndex(
      (record: Route) => clearPath(record.path) === clearPath(path)
    );

    if (index > 0) {
      this.$router.go(-index);
    } else {
      this.$router.push(path);
    }
  }

  /**
   * Вернуться к предыдущему логическому виду(по имени)
   */
  returnToParentViewByName(name: string): void {
    const index = this.$router.$history.findIndex(
      (record: Route) => record.name === name
    );

    if (index > 0) {
      this.$router.go(-index);
    } else {
      this.$router.push({ name });
    }
  }

  /**
   * 
   * @param {RouteLikeOrPath[]} parentRotes Список route-like объеков или путей
   * @param {RouteLikeOrPath} defaultRoute route-like объект или путь по умолчанию
   * @returns {RouteLikeOrPath} 
   */
  getParentRoute(
    parentRotes: RouteLikeOrPath[],
    defaultRoute: RouteLikeOrPath = {}
  ): RouteLikeOrPath {
    const parrentRoute: Route | undefined = this.$router.$history.find((route: Route) => {
      if (normalizePath(this.$route.path) === normalizePath(route.path) || !route.path) return false;

      return parentRotes.reduce((res, parent) => {
        const path = isString(parent) ? parent : parent.path || '';
        const reg = new RegExp(`^${normalizePath(path)}`);
        return res || reg.test(normalizePath(route.path));
      }, false);
    });
    if (parrentRoute) {
      return {
        path: parrentRoute.fullPath
      }
    }
    return defaultRoute;
  }

  /**
   * Сделать шаг назад в истории, если не куда, то по ссылке, по возможности сохраняя фильтры в url
   * пропуская ссылки на текущую страницу и степер
   * ограничение заложено во вью роутер на 15 ссылок в памяти
   */
  goBackOrToParentView(view: RouteLikeOrPath): void {
    let historyId = 0;
    this.$router.$history.forEach((historyElement: Route, index: number) => {
      if (
        historyElement.fullPath !== this.$route.fullPath &&
        historyElement.name !== 'stepper' &&
        !historyId
      ) {
        historyId = index;
      }
    });

    // Если есть id предыдущего пути, то возвращаемся по нему
    if (historyId) {
      this.$router.go(-historyId);
    } else {
      this.returnToParentView(view);
    }
  }
}

/**
 * Нормализирует путь для удобства сравнения
 * @param path Некий путь
 * @returns Путь начинающийся со слеша
 */
function normalizePath(path: string) {
  return path.replace(/^\/?/, '/');
}

/**
 * Удаление слэшей из пути
 */
function clearPath(s: string): string {
  return s.replace(/\//gi, '');
}
