import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { customRoutes } from '../src/router/routes/custom-routes.ts';
import { generatedRoutes } from '../src/router/elegant/routes.ts';
import zhCn from '../src/locales/langs/zh-cn.ts';

const currentFilePath = fileURLToPath(import.meta.url);
const currentDirPath = path.dirname(currentFilePath);
const rootDir = path.resolve(currentDirPath, '..');
const outputPath = path.resolve(rootDir, 'docs/frontend-page-resource-manifest.json');
const excludedPageResourceRouteNames = new Set(['exception_403', 'exception_404', 'exception_500']);
const excludedPageResourcePathPrefixes = ['/function/', '/plugin/'];

function isConstantRoute(route) {
  return Boolean(route.meta?.constant);
}

function getRouteOrder(route) {
  const order = Number(route.meta?.order);

  if (Number.isFinite(order)) {
    return order;
  }

  return Number.MAX_SAFE_INTEGER;
}

function sortRoutes(routes) {
  return [...routes].sort((next, prev) => {
    const orderDiff = getRouteOrder(next) - getRouteOrder(prev);

    if (orderDiff !== 0) {
      return orderDiff;
    }

    return String(next.path || '').localeCompare(String(prev.path || ''));
  });
}

function getPageComponent(component) {
  if (!component) {
    return null;
  }

  if (component.startsWith('view.')) {
    return component;
  }

  if (component.startsWith('layout.') && component.includes('$view.')) {
    return component;
  }

  return null;
}

function getPageType(component) {
  if (!component) {
    return null;
  }

  if (component.startsWith('view.')) {
    return 'leaf';
  }

  if (component.startsWith('layout.') && component.includes('$view.')) {
    return 'single';
  }

  return null;
}

function shouldExcludePageResource(route) {
  if (excludedPageResourceRouteNames.has(route.name)) {
    return true;
  }

  return excludedPageResourcePathPrefixes.some(prefix => String(route.path || '').startsWith(prefix));
}

function getNumberMetaValue(value) {
  const parsed = Number(value);

  if (Number.isFinite(parsed)) {
    return parsed;
  }

  return null;
}

function normalizeRouteProps(props) {
  if (props === true) {
    return true;
  }

  if (props && typeof props === 'object') {
    return props;
  }

  return null;
}

function getRouteLabel(route) {
  const routeLocales = zhCn.route || {};
  const i18nKey = route.meta?.i18nKey;

  if (typeof i18nKey === 'string' && i18nKey.startsWith('route.')) {
    const localeKey = i18nKey.slice('route.'.length);

    if (routeLocales[localeKey]) {
      return routeLocales[localeKey];
    }
  }

  if (routeLocales[route.name]) {
    return routeLocales[route.name];
  }

  return route.meta?.title || route.name;
}

function getPageResourceMeta(route) {
  const hideInMenu = Boolean(route.meta?.hideInMenu);
  const order = getNumberMetaValue(route.meta?.order);
  const fixedIndexInTab = getNumberMetaValue(route.meta?.fixedIndexInTab);

  return {
    title: getRouteLabel(route),
    i18nKey: route.meta?.i18nKey || null,
    icon: route.meta?.icon || null,
    localIcon: route.meta?.localIcon || null,
    order,
    keepAlive: Boolean(route.meta?.keepAlive),
    hideInMenu,
    activeMenu: route.meta?.activeMenu || null,
    multiTab: Boolean(route.meta?.multiTab),
    fixedIndexInTab
  };
}

function shouldCollectPageResource(route, pageComponent) {
  if (!pageComponent) {
    return false;
  }

  if (route.meta?.hideInMenu) {
    return false;
  }

  return !shouldExcludePageResource(route);
}

function createPageResourceItem(route, options) {
  const { pageComponent, parentName, source } = options;
  const props = normalizeRouteProps(route.props);
  const meta = getPageResourceMeta(route);

  return {
    name: route.name,
    path: route.path,
    component: pageComponent,
    title: meta.title,
    routeTitle: route.meta?.title || route.name,
    i18nKey: meta.i18nKey,
    icon: meta.icon,
    localIcon: meta.localIcon,
    order: meta.order,
    hideInMenu: meta.hideInMenu,
    keepAlive: meta.keepAlive,
    activeMenu: meta.activeMenu,
    multiTab: meta.multiTab,
    fixedIndexInTab: meta.fixedIndexInTab,
    redirect: route.redirect || null,
    props,
    meta,
    parentName,
    pageType: getPageType(pageComponent),
    source
  };
}

function collectPageResources(routes, options, items) {
  const { parentName = null, source } = options;
  const orderedRoutes = sortRoutes(routes);

  for (const route of orderedRoutes) {
    const pageComponent = getPageComponent(route.component);

    // The backend page-node whitelist only contains menu-visible pages.
    if (shouldCollectPageResource(route, pageComponent)) {
      items.push(createPageResourceItem(route, { pageComponent, parentName, source }));
    }

    if (route.children?.length) {
      collectPageResources(route.children, { parentName: route.name, source }, items);
    }
  }
}

function getPageResources() {
  const items = [];

  collectPageResources(
    customRoutes.filter(route => !isConstantRoute(route)),
    { source: 'custom' },
    items
  );
  collectPageResources(
    generatedRoutes.filter(route => !isConstantRoute(route)),
    { source: 'generated' },
    items
  );

  const uniqueItems = Array.from(new Map(items.map(item => [item.name, item])).values());

  return uniqueItems;
}

const pageResources = getPageResources();

const result = {
  generatedAt: new Date().toISOString(),
  description: 'Frontend visible page resource whitelist for backend route/menu configuration.',
  rules: {
    directoryComponent: 'layout.base',
    pageComponentPattern: 'view.<routeName>',
    singlePageComponentPattern: 'layout.<layoutName>$view.<routeName>'
  },
  total: pageResources.length,
  items: pageResources
};

fs.writeFileSync(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8');

console.log(`Generated ${pageResources.length} page resources -> ${path.relative(rootDir, outputPath)}`);
