/**
 * 将 index.tsx 中的 defineHashPageRoute 页面同步到 annotation-source.json 的 directory.route 节点。
 *
 * 用法：
 *   node scripts/sync-annotation-directory.mjs
 *   node scripts/sync-annotation-directory.mjs --prototype xll-miniapp
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import ts from 'typescript';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PAGE_ID_RE = /^[a-z0-9-]+$/u;

function parseArgs(argv) {
  const args = { prototype: '', all: false, projectRoot: '' };
  for (let i = 0; i < argv.length; i += 1) {
    const arg = argv[i];
    if (arg === '--prototype' && argv[i + 1]) {
      args.prototype = argv[++i].trim();
    } else if (arg === '--project-root' && argv[i + 1]) {
      args.projectRoot = path.resolve(argv[++i].trim());
    } else if (arg === '--all') {
      args.all = true;
    }
  }
  return args;
}

function readJson(filePath) {
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}

function writeJson(filePath, value) {
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
}

function normalizePageId(value) {
  const id = typeof value === 'string' ? value.trim() : '';
  return PAGE_ID_RE.test(id) ? id : '';
}

function getLiteralPropertyValue(objectLiteral, propertyName) {
  const property = objectLiteral.properties.find((candidate) => (
    ts.isPropertyAssignment(candidate)
    && (
      (ts.isIdentifier(candidate.name) && candidate.name.text === propertyName)
      || (ts.isStringLiteral(candidate.name) && candidate.name.text === propertyName)
    )
  ));
  if (!property || !ts.isPropertyAssignment(property)) {
    return null;
  }
  const initializer = property.initializer;
  return ts.isStringLiteral(initializer) || ts.isNoSubstitutionTemplateLiteral(initializer)
    ? initializer.text.trim()
    : null;
}

function extractHashRouteFromCall(callExpression) {
  const expression = callExpression.expression;
  if (!ts.isIdentifier(expression) || expression.text !== 'defineHashPageRoute') {
    return null;
  }
  const pagesArg = callExpression.arguments[0];
  if (!pagesArg || !ts.isArrayLiteralExpression(pagesArg)) {
    return null;
  }
  const pages = [];
  for (const element of pagesArg.elements) {
    if (!ts.isObjectLiteralExpression(element)) continue;
    const id = normalizePageId(getLiteralPropertyValue(element, 'id'));
    const title = getLiteralPropertyValue(element, 'title');
    if (id && title) pages.push({ id, title });
  }
  if (!pages.length) return null;
  const optionsArg = callExpression.arguments[1];
  const requestedDefaultPageId = optionsArg && ts.isObjectLiteralExpression(optionsArg)
    ? normalizePageId(getLiteralPropertyValue(optionsArg, 'defaultPageId'))
    : '';
  const defaultPageId = pages.some((page) => page.id === requestedDefaultPageId)
    ? requestedDefaultPageId
    : pages[0].id;
  return { pages, defaultPageId };
}

function extractHashRouteFromPrototype(prototypeDir) {
  const indexFile = path.join(prototypeDir, 'index.tsx');
  if (!fs.existsSync(indexFile)) return null;
  const sourceText = fs.readFileSync(indexFile, 'utf8');
  const sourceFile = ts.createSourceFile(indexFile, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
  let route = null;
  const visit = (node) => {
    if (route) return;
    if (ts.isCallExpression(node)) {
      route = extractHashRouteFromCall(node);
      if (route) return;
    }
    ts.forEachChild(node, visit);
  };
  visit(sourceFile);
  return route;
}

function loadDirectoryPagesConfig(prototypeDir) {
  const configPath = path.join(prototypeDir, 'directory.pages.json');
  if (!fs.existsSync(configPath)) return null;
  return readJson(configPath);
}

function routeNodeId(route) {
  return `route-${String(route).replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase()}`;
}

function createRouteNode(route, title) {
  return {
    type: 'route',
    id: routeNodeId(route),
    title,
    route,
  };
}

function collectRouteNodes(nodes, result = []) {
  for (const node of nodes || []) {
    if (node?.type === 'route') result.push(node);
    if (node?.type === 'folder' && Array.isArray(node.children)) {
      collectRouteNodes(node.children, result);
    }
  }
  return result;
}

function resolveRouteToken(pageId, existingRoutes) {
  const byId = existingRoutes.find((node) => node.id === routeNodeId(pageId));
  if (byId) return byId.route;
  const camel = pageId.replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
  const byCamel = existingRoutes.find((node) => node.route === camel);
  if (byCamel) return byCamel.route;
  return pageId;
}

function buildRouteFolders(pages, config, existingDirectory) {
  const existingRoutes = collectRouteNodes(existingDirectory?.nodes);
  const extraRoutes = Array.isArray(config?.extraRoutes) ? config.extraRoutes : [];
  const groups = Array.isArray(config?.groups) && config.groups.length
    ? config.groups
    : [{
      folderId: 'directory-pages',
      folderTitle: '页面导航',
      defaultExpanded: true,
      pageIds: pages.map((page) => page.id),
    }];

  const assigned = new Set();
  const folders = [];

  for (const group of groups) {
    const pageIds = Array.isArray(group.pageIds) ? group.pageIds : [];
    const children = [];
    for (const pageId of pageIds) {
      const page = pages.find((item) => item.id === pageId);
      if (!page) continue;
      assigned.add(page.id);
      children.push(createRouteNode(resolveRouteToken(page.id, existingRoutes), page.title));
    }
    for (const extra of extraRoutes) {
      if (extra.folderId !== group.folderId) continue;
      const route = String(extra.route || '').trim();
      const title = String(extra.title || '').trim();
      if (!route || !title) continue;
      children.push(createRouteNode(route, title));
      assigned.add(`extra:${route}`);
    }
    if (!children.length) continue;
    folders.push({
      type: 'folder',
      id: group.folderId || `directory-group-${folders.length + 1}`,
      title: group.folderTitle || '页面',
      defaultExpanded: group.defaultExpanded !== false,
      children,
    });
  }

  const unassigned = pages.filter((page) => !assigned.has(page.id));
  if (unassigned.length) {
    folders.push({
      type: 'folder',
      id: 'directory-pages-extra',
      title: '其他页面',
      defaultExpanded: true,
      children: unassigned.map((page) => createRouteNode(resolveRouteToken(page.id, existingRoutes), page.title)),
    });
  }

  const coveredExtras = new Set(
    extraRoutes.filter((extra) => groups.some((group) => group.folderId === extra.folderId)).map((extra) => extra.route),
  );
  const orphanExtras = extraRoutes.filter((extra) => !coveredExtras.has(extra.route));
  if (orphanExtras.length) {
    folders.push({
      type: 'folder',
      id: 'directory-routes-extra',
      title: '补充入口',
      defaultExpanded: true,
      children: orphanExtras.map((extra) => createRouteNode(extra.route, extra.title)),
    });
  }

  return folders;
}

function preserveFolders(directory, preserveFolderIds) {
  const ids = new Set(preserveFolderIds || []);
  return (directory?.nodes || []).filter((node) => node?.type === 'folder' && ids.has(node.id));
}

function fixPublishedLinks(nodes, prototypeId) {
  const walk = (items) => {
    for (const node of items || []) {
      if (node?.type === 'link' && typeof node.href === 'string') {
        const href = node.href.trim();
        const selfMatch = href.match(new RegExp(`^/prototypes/${prototypeId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:[#?].*)?$`, 'u'));
        if (selfMatch) {
          node.href = './index.html';
          node.target = node.target || 'self';
        } else {
          const otherMatch = href.match(/^\/prototypes\/([^/?#]+)(?:[#?].*)?$/u);
          if (otherMatch) {
            node.href = `../${otherMatch[1]}/index.html`;
            node.target = node.target || 'self';
          }
        }
      }
      if (node?.type === 'folder' && Array.isArray(node.children)) {
        walk(node.children);
      }
    }
  };
  walk(nodes);
}

function syncPrototypeDirectory(prototypeId, projectRoot) {
  const prototypesRoot = path.join(projectRoot, 'src/prototypes');
  const prototypeDir = path.join(prototypesRoot, prototypeId);
  const annotationPath = path.join(prototypeDir, 'annotation-source.json');
  if (!fs.existsSync(annotationPath)) {
    console.log(`[skip] ${prototypeId}: 无 annotation-source.json`);
    return false;
  }

  const annotation = readJson(annotationPath);
  const route = extractHashRouteFromPrototype(prototypeDir);
  const config = loadDirectoryPagesConfig(prototypeDir);
  const preserveFolderIds = config?.preserveFolderIds || ['directory-docs'];
  const preserved = preserveFolders(annotation.directory, preserveFolderIds);

  if (route?.pages?.length) {
    const routeFolders = buildRouteFolders(route.pages, config, annotation.directory);
    annotation.directory = {
      nodes: [...routeFolders, ...preserved],
    };
    annotation.data = annotation.data || {};
    annotation.data.pageId = route.defaultPageId;
    annotation.data.updatedAt = Date.now();
    console.log(`[sync] ${prototypeId}: 已同步 ${route.pages.length} 个页面到原型目录`);
  } else {
    console.log(`[keep] ${prototypeId}: 无 hash 路由，仅修复发布链接`);
  }

  fixPublishedLinks(annotation.directory?.nodes, prototypeId);
  writeJson(annotationPath, annotation);
  return true;
}

function listPrototypeIds(projectRoot) {
  const prototypesRoot = path.join(projectRoot, 'src/prototypes');
  if (!fs.existsSync(prototypesRoot)) return [];
  return fs.readdirSync(prototypesRoot, { withFileTypes: true })
    .filter((entry) => entry.isDirectory() && fs.existsSync(path.join(prototypesRoot, entry.name, 'annotation-source.json')))
    .map((entry) => entry.name)
    .sort((a, b) => a.localeCompare(b));
}

function main() {
  const args = parseArgs(process.argv.slice(2));
  const projectRoot = args.projectRoot || path.resolve(__dirname, '..');
  const targets = args.prototype
    ? [args.prototype]
    : listPrototypeIds(projectRoot);

  if (!targets.length) {
    console.log('未找到带 annotation-source.json 的原型。');
    process.exit(0);
  }

  let changed = 0;
  for (const prototypeId of targets) {
    if (syncPrototypeDirectory(prototypeId, projectRoot)) changed += 1;
  }
  console.log(`完成：处理 ${targets.length} 个原型，更新 ${changed} 个 annotation-source.json`);
}

main();
