import VueRouter, { RouterOptions } from 'vue-router';
import Vue from 'vue';
// @ts-ignore // FIXME Преобразовать в *.ts
import routes from '@router/routes';
import store from '@state/store';
import { get, sortBy, isEmpty, has, last } from 'lodash';
import dashboardRoutes from '@router/dashboard/routes';
import performancePanel from '@router/performance-panel/routes';
import scopesRoutes from '@router/scopes/routes';
import solutionsRoutes from '@router/solutions/routes';
import tasksRoutes from '@router/tasks/routes';
import userTasksRoutes from '@router/user-tasks/routes';
import envelopesRoutes from '@router/envelopes/routes';
import resourcesRoutes from '@router/resources/routes';
import processesRoutes from '@router/processes/routes';
import statementsRoutes from '@router/statements/routes';
import statementDraftsRoutes from '@router/statement-drafts/routes';
import recruitRoutes from '@/recruits/routes';
import personalAccounts from '@router/personal-accounts/routes';
import printTemplates from '@router/print-templates/routes';
import registryUnloading from '@router/registryUnloading/routes';
import registries from '@router/registries/routes';
import tickets from '@router/tickets/routes';
import services from '@router/services/routes';
import backgroundInfo from '@router/background-info/routes';
import debugCode from '@router/debug-code/routes';
import additionalSettings from '@router/additional-settings/routes';
import notifications from '@router/notifications/routes';
import administration from '@router/administration/routes';
import regionsRoutes from '@router/regions/routes';
import reportsRoutes from '@router/reports/routes';
import incidentsRoutes from '@router/incidents/routes';
import criteriaDirectoryRoutes from '@router/criteria-directory/routes';
import {
  RouteRecord,
  RouteConfig as VueRouterConfig,
} from 'vue-router/types/router';
import { matchScopes, Scope, ScopesMode } from '@utils/scopes';
import muRoutes from '@router/release-script/routes';
import signedApplicationsRoutes from '@router/signed-applications/routes';
import registryUkranianRefugeePaymentRoutes from '@router/registry-ukranian-refugee-payment/routes';
import directory from '@router/directory/routes';
import army from '@router/army/routes';
import interagencyRequestsRoutes from '@router/interagency-requests/routes';
import notificationTemplates from '@router/notificationTemplates/routes';
import convertedDocumentDownload from '@router/converted-document-download/routes';

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
} as RouterOptions);
router.onError(() => {
  router.push('start');
});

Vue.use(VueRouter);

type RouteConfig = VueRouterConfig & {
  fullPath?: string;
  hasClickableChildren?: boolean;
  accumulatedStoreCountGetters?: Array<string>;
};

const mainLayoutRoute: RouteConfig = get(routes, '[0].children[0]');
const availableRoutes = [
  ...dashboardRoutes,
  ...performancePanel,
  ...tasksRoutes,
  // ...userTasksRoutes,
  ...resourcesRoutes,
  ...scopesRoutes,
  ...solutionsRoutes,
  ...envelopesRoutes,
  // ...processesRoutes,
  ...statementsRoutes,
  ...statementDraftsRoutes,
  ...recruitRoutes,
  ...interagencyRequestsRoutes,
  ...registryUnloading,
  ...registryUkranianRefugeePaymentRoutes,
  ...registries,
  ...tickets,
  // ...services,
  ...backgroundInfo,
  ...debugCode,
  ...additionalSettings,
  ...notifications,
  ...administration,
  ...regionsRoutes,
  ...reportsRoutes,
  ...personalAccounts,
  ...printTemplates,
  ...incidentsRoutes,
  ...muRoutes,
  ...signedApplicationsRoutes,
  ...directory,
  ...army,
  ...criteriaDirectoryRoutes,
  ...notificationTemplates,
  ...convertedDocumentDownload,
];

store.dispatch('oidcClient/initOidcClient').finally(async () => {
  if (store.getters['oidcClient/isLoggedIn']) {
    const mainLayoutRouteChildren = availableRoutes.flatMap((route) => {
      return shouldRouteBeHiddenByScopes(route) ? [] : route;
    });
    mainLayoutRoute.children = mainLayoutRouteChildren;
    router.addRoutes([mainLayoutRoute]);
  }
});

router.beforeEach((to, from, next) => {
  const route = last(to.matched);
  // Если у пользователя нет прав или раздел выключен, перекидываем его на стартовую страницу
  if (
    route &&
    (shouldRouteBeHiddenByScopes(route) || get(route, 'meta.disabled'))
  ) {
    next({ name: 'start' });
  } else {
    next();
  }
});

export { router };

function concatenatePath(...folders: string[]): string {
  return folders
    .reduce((acc, next) => {
      return `${acc}/${next}`;
    }, '')
    .replace(/[/]+/g, '/');
}

function shouldRouteBeHiddenInProd(route: RouteConfig): boolean {
  // дело в том что это флажок с наизнанку №@#&^% вывернутой логикой,
  // showProd = false может спрятать елемент, а если его нет то ничего не прятать
  if (route?.meta?.showProd === undefined) return false;
  return !route.meta.showProd;
}

function shouldRouteBeHiddenByScopes(
  route: RouteConfig | RouteRecord
): boolean {
  const requiredScopes: Scope = get(route, 'meta.scopes');
  if (isEmpty(requiredScopes)) return false;
  return !matchScopes(requiredScopes.list, requiredScopes.mode);
}
// скрываем серые роуты по проверке
const hideDisabledRoutes =
  Vue.prototype.$appConfig.HIDE_DISABLED_MENU_ITEMS === 'true';

function shouldRouteBeHiddenByDictionary(
  route: RouteConfig | RouteRecord
): boolean {
  return hideDisabledRoutes && !!get(route, 'meta.disabled');
}

function shouldRouteBeHiddenByWhiteList(route: RouteConfig): boolean {
  const drawerWhiteList: Scope['list'] = get(route, 'meta.drawerWhiteList');
  if (drawerWhiteList === undefined) return false;
  return !matchScopes(drawerWhiteList, ScopesMode.or);
}

// достаёт все route'ы с указанным place-ом которые являются вторым уровнем по иерархии,
// не взирая на вложенность route-ов
// внутри найденных элементов дальше уже не смотрят так как это следующий уровень
export function getSubMenuItems(
  routes: RouteConfig[],
  place = 'drawer',
  parentPath = ''
): RouteConfig[] {
  // Находим всех потомков текущего уровня не взирая на вложенность route-ов но при этом без посредников
  const subMenus: RouteConfig[] = routes.flatMap((route) => {
    const fullPath = concatenatePath(parentPath, route.path);
    if (route.meta?.place !== place) {
      return getSubMenuItems(route.children || [], place, fullPath);
    }

    // Вычищаем разделы скрытые старым дедовским способом
    if (shouldRouteBeHiddenByWhiteList(route)) return [];
    // Вычищаем разделы недоступные пользователю
    if (shouldRouteBeHiddenByScopes(route)) return [];

    if (shouldRouteBeHiddenByDictionary(route)) return [];
    // Вычищаем разделы недоступные на проде
    if (shouldRouteBeHiddenInProd(route)) return [];
    if (route.children !== undefined) {
      const children = getSubMenuItems(route.children, place, fullPath);
      const accumulatedStoreCountGetters = children.reduce(
        (acc: Array<string>, next: RouteConfig) => {
          if (next.meta?.storeCountGetters) {
            acc.push(next.meta.storeCountGetters);
          }
          if (next.accumulatedStoreCountGetters) {
            next.accumulatedStoreCountGetters.forEach((getter: string) =>
              acc.push(getter)
            );
          }
          return acc;
        },
        []
      );
      const hasClickableChildren = children.some(
        (r) => r.meta?.clickable === undefined || r.meta?.clickable === true
      );
      route = {
        ...route,
        fullPath,
        hasClickableChildren,
        accumulatedStoreCountGetters,
        children,
      };
    }
    return [route];
  });
  return sortBy(subMenus, 'meta.order');
}
