import type { Router } from 'vue-router';

import { LOGIN_PATH } from '@easyflow/constants';
import { preferences } from '@easyflow/preferences';
import { useAccessStore, useUserStore } from '@easyflow/stores';
import { startProgress, stopProgress } from '@easyflow/utils';

import { accessRoutes, coreRouteNames } from '#/router/routes';
import { useAuthStore } from '#/store';
import {
  buildForcePasswordRoute,
  isForcePasswordRoute,
  notifyForcePasswordChange,
  shouldForcePasswordChange,
} from '#/utils/password-reset';

import { generateAccess } from './access';
import {
  getDevLoginAccount,
  removeDevLoginQuery,
  shouldAttemptDevLogin,
} from './dev-login';

interface NetworkConnectionLike {
  effectiveType?: string;
  saveData?: boolean;
}

const CHUNK_ERROR_RELOAD_KEY = '__easyflow_chunk_error_reload_path__';

function isKnowledgeShareRoute(path: string) {
  return path === '/share/knowledge' || path === '/share/knowledge/expired';
}

function isSlowNetworkConnection() {
  if (typeof navigator === 'undefined') {
    return false;
  }
  const nav = navigator as Navigator & {
    connection?: NetworkConnectionLike;
    mozConnection?: NetworkConnectionLike;
    webkitConnection?: NetworkConnectionLike;
  };
  const connection =
    nav.connection ?? nav.mozConnection ?? nav.webkitConnection;
  if (!connection) {
    return false;
  }
  if (connection.saveData) {
    return true;
  }
  return ['2g', '3g', 'slow-2g'].includes(connection.effectiveType ?? '');
}

function isDynamicImportChunkError(error: unknown) {
  const message = String((error as Error | undefined)?.message ?? error ?? '');
  if (!message) {
    return false;
  }
  return (
    /Loading chunk/i.test(message) ||
    /Importing a module script failed/i.test(message) ||
    /Failed to fetch dynamically imported module/i.test(message) ||
    /dynamically imported module/i.test(message)
  );
}

function setupChunkErrorGuard(router: Router) {
  router.onError((error, to) => {
    if (!isDynamicImportChunkError(error)) {
      return;
    }
    const fullPath = to?.fullPath;
    if (!fullPath) {
      return;
    }

    const lastReloadPath = sessionStorage.getItem(CHUNK_ERROR_RELOAD_KEY);
    if (lastReloadPath === fullPath) {
      sessionStorage.removeItem(CHUNK_ERROR_RELOAD_KEY);
      return;
    }
    sessionStorage.setItem(CHUNK_ERROR_RELOAD_KEY, fullPath);
    window.location.assign(fullPath);
  });

  router.isReady().finally(() => {
    const reloadedPath = sessionStorage.getItem(CHUNK_ERROR_RELOAD_KEY);
    if (reloadedPath && router.currentRoute.value.fullPath === reloadedPath) {
      sessionStorage.removeItem(CHUNK_ERROR_RELOAD_KEY);
    }
  });
}

function shouldUseRouteProgress() {
  if (preferences.transition.progress) {
    return true;
  }
  // 普通网络下，loading遮罩不显示时自动回退到顶部进度条
  return preferences.transition.loading && !isSlowNetworkConnection();
}

/**
 * 通用守卫配置
 * @param router
 */
function setupCommonGuard(router: Router) {
  // 记录已经加载的页面
  const loadedPaths = new Set<string>();

  router.beforeEach((to) => {
    to.meta.loaded = loadedPaths.has(to.path);

    // 页面加载进度条
    if (!to.meta.loaded && shouldUseRouteProgress()) {
      startProgress();
    }
    return true;
  });

  router.afterEach((to, _from, failure) => {
    // 记录页面是否加载,如果已经加载，后续的页面切换动画等效果不在重复执行
    if (!failure) {
      loadedPaths.add(to.path);
    }

    // 关闭页面加载进度条
    if (shouldUseRouteProgress()) {
      stopProgress();
    }
  });
}

/**
 * 权限访问守卫配置
 * @param router
 */
function setupAccessGuard(router: Router) {
  let devLoginPromise: null | Promise<void> = null;

  router.beforeEach(async (to, from) => {
    const accessStore = useAccessStore();
    const userStore = useUserStore();
    const authStore = useAuthStore();
    const devLoginAccount = getDevLoginAccount(to.query);

    const resolveCleanFullPath = () =>
      router.resolve({
        hash: to.hash,
        path: to.path,
        query: removeDevLoginQuery(to.query),
      }).fullPath;

    if (devLoginAccount && import.meta.env.DEV && accessStore.accessToken) {
      return resolveCleanFullPath();
    }

    if (
      shouldAttemptDevLogin({
        account: devLoginAccount,
        hasAccessToken: !!accessStore.accessToken,
        isDev: import.meta.env.DEV,
      })
    ) {
      const account = devLoginAccount;
      try {
        devLoginPromise ??= authStore
          .authDevLogin(account ?? '')
          .then(() => undefined)
          .finally(() => {
            devLoginPromise = null;
          });
        await devLoginPromise;
        const cleanFullPath = resolveCleanFullPath();
        if (window.location.search.includes('devLogin=')) {
          window.location.replace(cleanFullPath);
          return false;
        }
        return cleanFullPath;
      } catch {
        const cleanFullPath = resolveCleanFullPath();
        return {
          path: LOGIN_PATH,
          query:
            cleanFullPath === preferences.app.defaultHomePath
              ? {}
              : { redirect: encodeURIComponent(cleanFullPath) },
          replace: true,
        };
      }
    }

    // 基本路由，这些路由不需要进入权限拦截
    if (coreRouteNames.includes(to.name as string)) {
      if (to.path === LOGIN_PATH && accessStore.accessToken) {
        const currentUser =
          userStore.userInfo || (await authStore.fetchUserInfo());
        if (shouldForcePasswordChange(currentUser)) {
          return buildForcePasswordRoute();
        }
        return decodeURIComponent(
          (to.query?.redirect as string) ||
            userStore.userInfo?.homePath ||
            preferences.app.defaultHomePath,
        );
      }
      return true;
    }

    // accessToken 检查
    if (!accessStore.accessToken) {
      // 明确声明忽略权限访问权限，则可以访问
      if (to.meta.ignoreAccess) {
        return true;
      }

      // 没有访问权限，跳转登录页面
      if (to.fullPath !== LOGIN_PATH) {
        const cleanFullPath =
          devLoginAccount && import.meta.env.DEV
            ? resolveCleanFullPath()
            : to.fullPath;
        return {
          path: LOGIN_PATH,
          // 如不需要，直接删除 query
          query:
            cleanFullPath === preferences.app.defaultHomePath
              ? {}
              : { redirect: encodeURIComponent(cleanFullPath) },
          // 携带当前跳转的页面，登录后重新跳转该页面
          replace: true,
        };
      }
      return to;
    }

    const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
    if (shouldForcePasswordChange(userInfo) && !isForcePasswordRoute(to)) {
      if (from.name) {
        notifyForcePasswordChange();
      }
      return buildForcePasswordRoute();
    }

    if (isKnowledgeShareRoute(to.path)) {
      return true;
    }

    // 是否已经生成过动态路由
    if (accessStore.isAccessChecked) {
      return true;
    }

    // 页面菜单与按钮权限码是两套数据源。每次重新构建动态菜单时，
    // 同步刷新一次 accessCodes，避免后端权限模型调整后页面仍持有旧按钮权限。
    await authStore.fetchAccessCodes();

    // 生成路由表
    // 当前登录用户拥有的角色标识列表
    const userRoles = userInfo.roles ?? [];

    // 生成菜单和路由
    const { accessibleMenus, accessibleRoutes } = await generateAccess({
      roles: userRoles,
      router,
      // 则会在菜单中显示，但是访问会被重定向到403
      routes: accessRoutes,
    });

    // 保存菜单信息和路由信息
    accessStore.setAccessMenus(accessibleMenus);
    accessStore.setAccessRoutes(accessibleRoutes);
    accessStore.setIsAccessChecked(true);
    const redirectPath = (from.query.redirect ??
      (to.path === preferences.app.defaultHomePath
        ? userInfo.homePath || preferences.app.defaultHomePath
        : to.fullPath)) as string;

    return {
      ...router.resolve(decodeURIComponent(redirectPath)),
      replace: true,
    };
  });
}

/**
 * 项目守卫配置
 * @param router
 */
function createRouterGuard(router: Router) {
  /** 通用 */
  setupCommonGuard(router);
  /** 权限访问 */
  setupAccessGuard(router);
  /** chunk加载错误兜底 */
  setupChunkErrorGuard(router);
}

export { createRouterGuard };
