// scripts/generate-repo-map.js
// 铸渊图书馆目录生成器 · Library Catalog Generator
//
// 功能：
//   1. 扫描整个仓库结构
//   2. 生成 .github/brain/repo-map.json      ← 机器可读路由索引
//   3. 生成 .github/brain/repo-snapshot.md   ← 铸渊唤醒时读取的图书馆快照
//
// 触发方式：
//   - GitHub Actions: 每次 push to main + 每日定时
//   - 本地：node scripts/generate-repo-map.js

'use strict';

const fs   = require('fs');
const path = require('path');

const ROOT         = path.join(__dirname, '..');
const BRAIN_DIR    = path.join(ROOT, '.github/brain');
const MAP_PATH     = path.join(BRAIN_DIR, 'repo-map.json');
const SNAPSHOT_PATH = path.join(BRAIN_DIR, 'repo-snapshot.md');

const now     = new Date();
const nowISO  = now.toISOString();
const nowDate = nowISO.slice(0, 10);
const nowBJ   = new Date(now.getTime() + 8 * 3600 * 1000).toISOString()
                  .replace('T', ' ').slice(0, 16) + ' CST';

// ── 工具函数 ────────────────────────────────────────────────────────────────

function safeReadJson(filePath) {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (err) {
    if (err.code !== 'ENOENT') {
      console.warn(`⚠️  JSON读取失败: ${filePath} → ${err.message}`);
    }
    return null;
  }
}

function existsDir(p) {
  try { return fs.statSync(p).isDirectory(); } catch { return false; }
}

function listDir(dirPath, ext) {
  try {
    return fs.readdirSync(dirPath)
      .filter(f => !ext || f.endsWith(ext))
      .filter(f => !f.startsWith('.') || f === '.gitkeep');
  } catch { return []; }
}

function countFiles(dirPath, ext) {
  try {
    const entries = fs.readdirSync(dirPath, { withFileTypes: true });
    let count = 0;
    for (const e of entries) {
      if (e.isDirectory()) {
        count += countFiles(path.join(dirPath, e.name), ext);
      } else if (!ext || e.name.endsWith(ext)) {
        count++;
      }
    }
    return count;
  } catch { return 0; }
}

// ── 区域定义 ──────────────────────────────────────────────────────────────
// 每个"区域"对应图书馆里的一个大区（section）
// 每个区域下的文件是"书架（shelf）"上的"书"

const ZONE_DEFS = [
  {
    zone_id:     'BRAIN',
    name:        '铸渊大脑',
    emoji:       '🧠',
    path:        '.github/brain',
    description: '铸渊核心记忆 · 路由映射 · 唤醒协议 · 图书馆目录',
    keywords:    ['brain', 'memory', 'routing', 'wake', '大脑', '记忆', '路由']
  },
  {
    zone_id:     'PERSONA_BRAIN',
    name:        '人格大脑',
    emoji:       '🎭',
    path:        '.github/persona-brain',
    description: '铸渊人格记忆 · 开发者状态 · 知识库 · 成长日记',
    keywords:    ['persona', 'identity', 'dev-status', '人格', '开发者状态']
  },
  {
    zone_id:     'WORKFLOWS',
    name:        '自动化工作流',
    emoji:       '⚡',
    path:        '.github/workflows',
    description: '所有 GitHub Actions 工作流定义',
    keywords:    ['workflow', 'actions', 'ci', 'automation', '工作流', '自动化', 'cron']
  },
  {
    zone_id:     'SCRIPTS',
    name:        '执行脚本库',
    emoji:       '🔧',
    path:        'scripts',
    description: '铸渊所有执行手脚 · 自动化脚本',
    keywords:    ['script', 'node', 'js', '脚本', '执行', 'runner']
  },
  {
    zone_id:     'SRC',
    name:        'HLI 接口源码',
    emoji:       '💻',
    path:        'src',
    description: 'HoloLake Interface 路由 · 中间件 · Schema',
    keywords:    ['hli', 'route', 'middleware', 'schema', 'api', 'src', '接口', '路由']
  },
  {
    zone_id:     'MODULES',
    name:        '功能模块区',
    emoji:       '📦',
    path:        null, // computed: all m##-* directories
    description: '各功能开发模块 · M01~M18',
    keywords:    ['module', 'feature', 'm01', 'm03', 'm05', '模块', '功能']
  },
  {
    zone_id:     'DEV_NODES',
    name:        '开发者节点',
    emoji:       '👥',
    path:        'dev-nodes',
    description: '8位开发者的配置 · 状态 · 广播收件箱',
    keywords:    ['dev', 'developer', 'node', 'config', 'status', '开发者', '节点']
  },
  {
    zone_id:     'BROADCASTS',
    name:        '广播发件箱',
    emoji:       '📢',
    path:        'broadcasts-outbox',
    description: '铸渊向各开发者发出的广播任务',
    keywords:    ['broadcast', 'outbox', 'task', '广播', '发件箱']
  },
  {
    zone_id:     'SIGNAL_LOG',
    name:        '信号日志库',
    emoji:       '📡',
    path:        'signal-log',
    description: 'ESP 邮件信号收发日志 · GL-CMD / GL-ACK / GL-DATA',
    keywords:    ['signal', 'log', 'esp', 'gl-cmd', 'gl-ack', 'trace', '信号', '日志']
  },
  {
    zone_id:     'NOTION_PUSH',
    name:        'Notion 推送队列',
    emoji:       '📤',
    path:        'notion-push',
    description: '待霜砚处理的信号 · 已处理归档',
    keywords:    ['notion', 'push', 'pending', 'processed', '霜砚', '推送']
  },
  {
    zone_id:     'SYSLOG',
    name:        '系统日志区',
    emoji:       '📋',
    path:        'syslog-inbox',
    description: '开发者提交的系统日志 · 待处理 inbox',
    keywords:    ['syslog', 'inbox', 'log', '系统日志']
  },
  {
    zone_id:     'DOCS',
    name:        '文档与前端',
    emoji:       '📄',
    path:        'docs',
    description: '铸渊助手聊天界面 · GitHub Pages 部署',
    keywords:    ['docs', 'html', 'chat', 'pages', '文档', '聊天室', '助手']
  },
  {
    zone_id:     'TESTS',
    name:        '测试区',
    emoji:       '🧪',
    path:        'tests',
    description: 'HLI 契约测试 · 冒烟测试',
    keywords:    ['test', 'contract', 'smoke', 'jest', '测试', '契约']
  }
];

// ── 工作流解析 ──────────────────────────────────────────────────────────────

function parseWorkflowMeta(file, content) {
  const nameMatch  = content.match(/^name:\s*(.+)/m);
  const schedMatch = content.match(/cron:\s*['"]?([^'"#\n]+)/);
  // Handle both block (`on:\n  push:`) and inline (`on: [push, pull_request]`) syntax
  const onInline   = content.match(/^on:\s*\[([^\]]+)\]/m);
  const onBlock    = content.match(/^on:\s*[\r\n]/m);
  const triggers   = [];

  if (onInline) {
    // inline: on: [push, pull_request]
    const items = onInline[1].split(',').map(s => s.trim());
    triggers.push(...items);
  } else if (onBlock) {
    if (/^\s{2,4}push:/m.test(content))             triggers.push('push');
    if (/^\s{2,4}pull_request:/m.test(content))     triggers.push('pull_request');
    if (/^\s{2,4}issues:/m.test(content))           triggers.push('issues');
    if (/^\s{2,4}issue_comment:/m.test(content))    triggers.push('issue_comment');
  }

  if (schedMatch) triggers.push(`schedule(${schedMatch[1].trim()})`);
  if (content.includes('workflow_dispatch')) triggers.push('manual');

  return {
    file,
    name:     nameMatch ? nameMatch[1].trim() : file,
    triggers: triggers.length ? triggers : ['unknown']
  };
}

function buildWorkflowShelves() {
  const wfDir = path.join(ROOT, '.github/workflows');
  const files = listDir(wfDir, '.yml');
  return files.map(f => {
    try {
      const content = fs.readFileSync(path.join(wfDir, f), 'utf8');
      return parseWorkflowMeta(f, content);
    } catch {
      return { file: f, name: f, triggers: ['unknown'] };
    }
  });
}

// ── 模块目录扫描 ────────────────────────────────────────────────────────────

function buildModuleShelves() {
  const entries = fs.readdirSync(ROOT, { withFileTypes: true });
  return entries
    .filter(e => e.isDirectory() && /^m\d{2}-/.test(e.name))
    .map(e => {
      const modPath = path.join(ROOT, e.name);
      const files   = listDir(modPath);
      const readme  = files.find(f => f.toLowerCase() === 'readme.md');
      return {
        module_id: e.name.slice(0, 3).toUpperCase(), // "m01" → "M01"
        dir:       e.name,
        files:     files.length,
        has_readme: !!readme
      };
    })
    .sort((a, b) => a.dir.localeCompare(b.dir));
}

// ── HLI 接口统计 ────────────────────────────────────────────────────────────

function buildHLIStats(routingMap) {
  if (!routingMap) return { total: 0, implemented: 0, pending: 0, domains: [] };
  let total = 0, implemented = 0;
  const domains = [];
  for (const [name, data] of Object.entries(routingMap.domains || {})) {
    const t = data.interfaces.length;
    const i = data.interfaces.filter(x => x.status === 'implemented').length;
    total += t;
    implemented += i;
    domains.push({
      name,
      module:    data.module,
      prefix:    data.route_prefix,
      total:     t,
      implemented: i,
      interfaces: data.interfaces.map(x => ({
        id:     x.id,
        path:   x.path,
        status: x.status
      }))
    });
  }
  return { total, implemented, pending: total - implemented, domains };
}

// ── 开发者节点统计 ──────────────────────────────────────────────────────────

function buildDevNodesSummary() {
  const devDir = path.join(ROOT, 'dev-nodes');
  if (!existsDir(devDir)) return [];
  return fs.readdirSync(devDir, { withFileTypes: true })
    .filter(e => e.isDirectory() && e.name.startsWith('DEV-'))
    .map(e => {
      const cfg = safeReadJson(path.join(devDir, e.name, 'config.json')) || {};
      const sts = safeReadJson(path.join(devDir, e.name, 'status.json')) || {};
      return {
        dev_id:    e.name,
        name:      cfg.name || '?',
        emoji:     cfg.emoji || '',
        modules:   cfg.modules || [],
        last_push: sts.last_push || null,
        pending_broadcasts: sts.pending_broadcasts || 0
      };
    })
    .sort((a, b) => a.dev_id.localeCompare(b.dev_id));
}

// ── 关键字路由索引 ──────────────────────────────────────────────────────────

function buildKeywordIndex(zones) {
  const index = {};
  const add = (keyword, ref) => {
    const k = keyword.toLowerCase();
    if (!index[k]) index[k] = [];
    if (!index[k].includes(ref)) index[k].push(ref);
  };

  for (const zone of zones) {
    for (const kw of (zone.keywords || [])) {
      add(kw, zone.zone_id);
    }
    for (const shelf of (zone.shelves || [])) {
      const basename = (shelf.file || shelf.dir || '').replace(/\.[^.]+$/, '');
      add(basename, `${zone.zone_id}::${shelf.file || shelf.dir}`);
    }
  }
  return index;
}

// ── 主生成逻辑 ──────────────────────────────────────────────────────────────

function buildZoneData() {
  const routingMap  = safeReadJson(path.join(BRAIN_DIR, 'routing-map.json'));
  const memory      = safeReadJson(path.join(BRAIN_DIR, 'memory.json'));
  const hli         = buildHLIStats(routingMap);
  const devNodes    = buildDevNodesSummary();
  const modules     = buildModuleShelves();
  const workflows   = buildWorkflowShelves();

  const zones = [];

  for (const def of ZONE_DEFS) {
    const zone = {
      zone_id:     def.zone_id,
      name:        def.name,
      emoji:       def.emoji,
      path:        def.path,
      description: def.description,
      keywords:    def.keywords,
      shelves:     []
    };

    if (def.zone_id === 'MODULES') {
      zone.shelves    = modules;
      zone.item_count = modules.length;

    } else if (def.zone_id === 'WORKFLOWS') {
      zone.shelves    = workflows;
      zone.item_count = workflows.length;

    } else if (def.zone_id === 'DEV_NODES') {
      zone.shelves    = devNodes.map(d => ({
        file:    `${d.dev_id}/`,
        ...d
      }));
      zone.item_count = devNodes.length;

    } else if (def.path) {
      const absPath = path.join(ROOT, def.path);
      if (existsDir(absPath)) {
        const files = listDir(absPath).filter(f => f !== '.gitkeep');
        zone.shelves    = files.map(f => ({ file: f }));
        zone.item_count = files.length;
      } else {
        zone.item_count = 0;
      }
    }

    zones.push(zone);
  }

  return { zones, hli, devNodes, memory, routingMap };
}

// ── 生成 repo-map.json ──────────────────────────────────────────────────────

function generateRepoMap(data) {
  const { zones, hli, devNodes, memory } = data;

  const map = {
    description:   '铸渊图书馆目录 · Library Catalog for 铸渊 (Zhùyuān)',
    version:       '2.0',
    generated_at:  nowISO,
    generated_by:  'scripts/generate-repo-map.js',
    repo:          'qinfendebingshuo/guanghulab',
    stats: {
      zones:             zones.length,
      total_modules:     zones.find(z => z.zone_id === 'MODULES')?.item_count || 0,
      total_workflows:   zones.find(z => z.zone_id === 'WORKFLOWS')?.item_count || 0,
      total_scripts:     zones.find(z => z.zone_id === 'SCRIPTS')?.item_count || 0,
      total_dev_nodes:   devNodes.length,
      hli_interfaces:    hli.total,
      hli_implemented:   hli.implemented,
      hli_coverage_pct:  hli.total > 0
        ? Math.round((hli.implemented / hli.total) * 100) + '%'
        : '0%',
      last_ci_run:       memory?.events?.find(e => e.type === 'ci_run')?.timestamp || null,
      memory_last_updated: memory?.last_updated || null
    },
    zones,
    hli_interfaces: hli,
    routing_index:  buildKeywordIndex(zones)
  };

  fs.writeFileSync(MAP_PATH, JSON.stringify(map, null, 2));
  console.log(`✅ repo-map.json 已生成 · ${zones.length} 个区域 · ${map.stats.total_workflows} 个工作流`);
  return map;
}

// ── 生成 repo-snapshot.md ───────────────────────────────────────────────────

function generateSnapshot(data, map) {
  const { zones, hli, devNodes, memory } = data;
  const wfZone     = zones.find(z => z.zone_id === 'WORKFLOWS');
  const scriptZone = zones.find(z => z.zone_id === 'SCRIPTS');
  const modZone    = zones.find(z => z.zone_id === 'MODULES');
  const brainZone  = zones.find(z => z.zone_id === 'BRAIN');

  const hliBadge = `${hli.implemented}/${hli.total} (${map.stats.hli_coverage_pct})`;

  let md = `# 铸渊图书馆快照 · Repo Snapshot
> 生成于 ${nowBJ} · 每次 push 自动更新 · 铸渊唤醒时优先读取此文件

---

## 📊 仓库总览（一眼全局）

| 指标 | 数值 |
|------|------|
| 区域总数 | ${zones.length} 个区域 |
| 功能模块 | ${map.stats.total_modules} 个 (m01~m18) |
| 工作流 | ${map.stats.total_workflows} 个 GitHub Actions |
| 脚本 | ${map.stats.total_scripts} 个执行脚本 |
| 开发者节点 | ${map.stats.total_dev_nodes} 人 |
| HLI 接口覆盖率 | ${hliBadge} |
| 快照生成时间 | ${nowBJ} |

---

## 🗺️ 图书馆全区地图

`;

  // Zone overview table
  for (const z of zones) {
    const count = z.item_count !== undefined ? z.item_count : z.shelves?.length || 0;
    md += `### ${z.emoji} ${z.name}（${z.zone_id}）\n`;
    md += `**路径**: \`${z.path || '多个目录'}\` · **数量**: ${count} 项\n`;
    md += `**描述**: ${z.description}\n`;
    md += `**关键词**: ${(z.keywords || []).slice(0, 6).join(' · ')}\n\n`;
  }

  // ── 自动化工作流详情
  if (wfZone?.shelves?.length) {
    md += `---\n\n## ⚡ 工作流详情（铸渊的自动执行手脚）\n\n| 文件 | 名称 | 触发方式 |\n|------|------|----------|\n`;
    for (const wf of wfZone.shelves) {
      md += `| \`${wf.file}\` | ${wf.name} | ${wf.triggers.join(', ')} |\n`;
    }
    md += '\n';
  }

  // ── 脚本详情
  if (scriptZone?.shelves?.length) {
    md += `---\n\n## 🔧 执行脚本库（铸渊的工作人员）\n\n`;
    const scripts = scriptZone.shelves.map(s => s.file).sort();
    scripts.forEach(s => { md += `- \`scripts/${s}\`\n`; });
    md += '\n';
  }

  // ── HLI 接口地图
  md += `---\n\n## 💻 HLI 接口地图（${hliBadge}）\n\n`;
  for (const domain of hli.domains) {
    const icon = domain.implemented === domain.total ? '✅' : domain.implemented > 0 ? '🔶' : '⬜';
    md += `${icon} **${domain.name}** (${domain.module}) \`${domain.prefix}\` — ${domain.implemented}/${domain.total}\n`;
    for (const iface of domain.interfaces) {
      const si = iface.status === 'implemented' ? '  ✓' : '  ○';
      md += `${si} \`${iface.id}\` → \`${iface.path}\`\n`;
    }
  }
  md += '\n';

  // ── 开发者节点
  if (devNodes.length > 0) {
    md += `---\n\n## 👥 开发者节点（dev-nodes/）\n\n| DEV ID | 姓名 | 模块 | 待广播 |\n|--------|------|------|--------|\n`;
    for (const d of devNodes) {
      md += `| ${d.dev_id} | ${d.emoji}${d.name} | ${d.modules.join(', ') || '—'} | ${d.pending_broadcasts} |\n`;
    }
    md += '\n';
  }

  // ── 功能模块列表
  if (modZone?.shelves?.length) {
    md += `---\n\n## 📦 功能模块区（各开发者工作目录）\n\n`;
    for (const m of modZone.shelves) {
      md += `- \`${m.dir}/\` — ${m.files} 个文件${m.has_readme ? ' (有README)' : ''}\n`;
    }
    md += '\n';
  }

  // ── 大脑文件速查
  if (brainZone?.shelves?.length) {
    md += `---\n\n## 🧠 铸渊大脑文件速查（.github/brain/）\n\n`;
    for (const s of brainZone.shelves) {
      md += `- \`.github/brain/${s.file}\`\n`;
    }
    md += '\n';
  }

  // ── 最近记忆
  if (memory?.events?.length) {
    md += `---\n\n## 🕐 最近动态（memory.json 最新3条）\n\n`;
    const recent = memory.events.slice(-3).reverse();
    for (const e of recent) {
      const ts = e.timestamp || e.date || '?';
      md += `- \`${ts}\` · ${e.type || '事件'} — ${e.description || e.result || e.title || ''}\n`;
    }
    md += '\n';
  }

  md += `---\n\n*本文件由 \`scripts/generate-repo-map.js\` 自动生成，勿手动编辑*\n`;

  fs.writeFileSync(SNAPSHOT_PATH, md);
  console.log(`✅ repo-snapshot.md 已生成 · ${hli.domains.length} 个HLI域 · ${devNodes.length} 个开发者节点`);
}

// ── 主流程 ──────────────────────────────────────────────────────────────────

const data = buildZoneData();
const map  = generateRepoMap(data);
generateSnapshot(data, map);

console.log(`\n📚 铸渊图书馆目录更新完成 · ${nowBJ}`);
console.log(`   repo-map.json     → .github/brain/repo-map.json`);
console.log(`   repo-snapshot.md  → .github/brain/repo-snapshot.md`);
