# ============================================
# COS 行业开发审核桥接工作流
# COS Industry Development Review Bridge
# 签发: 铸渊 · ICE-GL-ZY001
# 版权: 国作登字-2026-A-00037559
# ============================================
#
# 功能:
#   1. 定时扫描COS桶中的行业开发提交
#   2. 自动审核开发方案(架构/安全/命名)
#   3. 生成审核回执并写入COS
#   4. (可选) 通过repository_dispatch触发对方仓库的副驾驶
#
# 触发方式:
#   - 定时: 每5分钟 (与COS Watcher同步)
#   - 手动: workflow_dispatch
#
# 架构文档: brain/age-os-landing/industry-access-architecture.md
# 协议规范: hldp/hnl/industry-bridge-protocol.json
# 租户注册表: server/proxy/config/industry-tenant-registry.json

name: COS开发审核桥接 · Dev Review Bridge

on:
  schedule:
    - cron: '*/5 * * * *'  # 每5分钟
  workflow_dispatch:
    inputs:
      scan_mode:
        description: '扫描模式'
        required: false
        default: 'auto'
        type: choice
        options:
          - auto       # 自动扫描所有行业
          - manual     # 手动指定行业和人格体
      industry_code:
        description: '行业代码 (manual模式必填)'
        required: false
        type: string
      persona_id:
        description: '人格体ID (manual模式必填)'
        required: false
        type: string
      dry_run:
        description: '仅审核不写回执'
        required: false
        default: false
        type: boolean

env:
  COS_BUCKET: zy-team-hub-1317346199
  COS_REGION: ap-guangzhou
  NODE_VERSION: '20'

permissions:
  contents: read

jobs:
  scan-and-review:
    name: 扫描 → 审核 → 回执
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: 🏔️ 铸渊唤醒 · 审核桥接启动
        run: |
          echo "============================================"
          echo "铸渊 · ICE-GL-ZY001"
          echo "COS行业开发审核桥接"
          echo "时间: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
          echo "模式: ${{ github.event.inputs.scan_mode || 'auto' }}"
          echo "============================================"

      - name: 📥 检出仓库
        uses: actions/checkout@v4

      - name: 🟢 Node.js 环境
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: 📦 安装COS SDK
        run: |
          npm install cos-nodejs-sdk-v5@2

      - name: 🔍 扫描COS桶 · 检测新的开发提交
        id: scan
        env:
          COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }}
          COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }}
        run: |
          node << 'SCAN_SCRIPT'
          const COS = require('cos-nodejs-sdk-v5');
          const fs = require('fs');
          const path = require('path');

          const cos = new COS({
            SecretId: process.env.COS_SECRET_ID,
            SecretKey: process.env.COS_SECRET_KEY
          });

          const BUCKET = '${{ env.COS_BUCKET }}';
          const REGION = '${{ env.COS_REGION }}';

          // 读取租户注册表
          const registry = JSON.parse(
            fs.readFileSync('server/proxy/config/industry-tenant-registry.json', 'utf8')
          );

          // 通过检查receipts目录判断提交是否已审核（持久化去重）
          async function getExistingReceipts(tenant) {
            const receiptPrefix = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/`;
            return new Promise((resolve) => {
              cos.getBucket({
                Bucket: BUCKET,
                Region: REGION,
                Prefix: receiptPrefix,
                MaxKeys: 1000
              }, (err, data) => {
                if (err) { resolve(new Set()); return; }
                // 从回执中提取已审核的submission_id集合
                // 回执文件名包含时间戳，无法直接对应，改为列出所有回执key
                const receiptKeys = new Set((data.Contents || []).map(item => item.Key));
                resolve(receiptKeys);
              });
            });
          }

          async function scanIndustry(tenant) {
            const prefix = `industry/${tenant.industry_code}/dev-submissions/${tenant.tech_controller.persona_id}/`;
            const receiptPrefix = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/`;
            console.log(`扫描: ${prefix}`);

            // 获取已有回执列表用于去重
            const existingReceipts = await getExistingReceipts(tenant);
            console.log(`已有回执数: ${existingReceipts.size}`);

            return new Promise((resolve, reject) => {
              cos.getBucket({
                Bucket: BUCKET,
                Region: REGION,
                Prefix: prefix,
                MaxKeys: 100
              }, (err, data) => {
                if (err) {
                  console.error(`扫描失败: ${prefix}`, err.message);
                  resolve([]);
                  return;
                }

                const allSubmissions = (data.Contents || [])
                  .filter(item => item.Key.endsWith('.json'));

                // 过滤已有回执的提交（通过检查submission文件名是否在receipts中被引用）
                // 简单策略：如果receipts数量 >= submissions数量，说明都已审核过
                // 精确策略：需要读取每个receipt的ref_submission_id，但开销大
                // 采用时间策略：只处理最近24小时内的提交
                const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
                const submissions = allSubmissions
                  .filter(item => item.LastModified > oneDayAgo)
                  .map(item => ({
                    key: item.Key,
                    lastModified: item.LastModified,
                    size: item.Size,
                    tenant: tenant
                  }));

                console.log(`发现 ${submissions.length} 个待审核提交 (24h内)`);
                resolve(submissions);
              });
            });
          }

          async function main() {
            const scanMode = '${{ github.event.inputs.scan_mode || 'auto' }}';
            let tenantsToScan = registry.tenants;

            if (scanMode === 'manual') {
              const industryCode = '${{ github.event.inputs.industry_code }}';
              const personaId = '${{ github.event.inputs.persona_id }}';
              tenantsToScan = registry.tenants.filter(t =>
                t.industry_code === industryCode &&
                t.tech_controller.persona_id === personaId
              );
            }

            let allSubmissions = [];
            for (const tenant of tenantsToScan) {
              const subs = await scanIndustry(tenant);
              allSubmissions = allSubmissions.concat(subs);
            }

            // 输出到GitHub Actions
            fs.writeFileSync('/tmp/new-submissions.json', JSON.stringify(allSubmissions, null, 2));
            const count = allSubmissions.length;
            console.log(`\n总计发现 ${count} 个新的开发提交`);

            // 设置输出
            const hasNew = count > 0 ? 'true' : 'false';
            fs.appendFileSync(process.env.GITHUB_OUTPUT, `has_new=${hasNew}\n`);
            fs.appendFileSync(process.env.GITHUB_OUTPUT, `count=${count}\n`);
          }

          main().catch(err => {
            console.error('扫描失败:', err);
            process.exit(1);
          });
          SCAN_SCRIPT

      - name: 📋 审核开发方案
        if: steps.scan.outputs.has_new == 'true'
        id: review
        env:
          COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }}
          COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }}
        run: |
          node << 'REVIEW_SCRIPT'
          const COS = require('cos-nodejs-sdk-v5');
          const fs = require('fs');

          const cos = new COS({
            SecretId: process.env.COS_SECRET_ID,
            SecretKey: process.env.COS_SECRET_KEY
          });

          const BUCKET = '${{ env.COS_BUCKET }}';
          const REGION = '${{ env.COS_REGION }}';
          const DRY_RUN = '${{ github.event.inputs.dry_run }}' === 'true';

          // 已知有CVE漏洞的依赖 (包名和可选版本)
          const KNOWN_VULNERABLE_DEPS = [
            { name: 'event-stream', version: null },
            { name: 'ua-parser-js', version: '0.7.28' },
            { name: 'colors', version: '1.4.1' },
            { name: 'faker', version: '6.6.6' },
            { name: 'node-ipc', version: '10.1.1' }
          ];

          // 禁止暴露的端口
          const FORBIDDEN_PORTS = [22, 3306, 5432, 6379, 27017];

          // 精确匹配依赖名称（处理 @scope/package@version 格式）
          function isVulnerableDep(depString) {
            const depLower = depString.toLowerCase().trim();
            // 解析包名和版本: @scope/package@version 或 package@version 或 package
            let depName, depVersion;
            const lastAtIndex = depLower.lastIndexOf('@');
            if (lastAtIndex > 0) {
              depName = depLower.substring(0, lastAtIndex);
              depVersion = depLower.substring(lastAtIndex + 1);
            } else {
              depName = depLower;
              depVersion = null;
            }

            return KNOWN_VULNERABLE_DEPS.some(vuln => {
              if (depName !== vuln.name.toLowerCase()) return false;
              if (vuln.version === null) return true; // 所有版本都有漏洞
              if (depVersion === null) return true; // 未指定版本，保守判定
              return depVersion === vuln.version;
            });
          }

          function reviewSubmission(submission) {
            const result = {
              architecture: 'PASS',
              security: 'PASS',
              naming: 'PASS',
              overall: 'APPROVED',
              suggestions: [],
              warnings: [],
              blockers: []
            };

            const payload = submission.payload || {};
            const data = payload.data || {};
            const impact = data.architecture_impact || {};

            // --- 架构审核 ---
            if (impact.database_changes && impact.database_changes.length > 0) {
              result.suggestions.push('数据库变更建议在测试环境先验证');
              for (const change of impact.database_changes) {
                if (change.toLowerCase().includes('persona_registry') ||
                    change.toLowerCase().includes('light_tree') ||
                    change.toLowerCase().includes('tianyan')) {
                  result.architecture = 'FAIL';
                  result.blockers.push(`禁止直接修改铸渊核心表: ${change}`);
                }
              }
            }

            if (impact.new_api_endpoints) {
              for (const endpoint of impact.new_api_endpoints) {
                if (endpoint.startsWith('/hli/')) {
                  result.architecture = 'FAIL';
                  result.blockers.push(`禁止在行业层使用 /hli/ 前缀(铸渊专用): ${endpoint}`);
                }
              }
            }

            // --- 安全审核 ---
            if (impact.external_dependencies) {
              for (const dep of impact.external_dependencies) {
                if (isVulnerableDep(dep)) {
                  result.security = 'FAIL';
                  result.blockers.push(`依赖有已知CVE漏洞: ${dep}`);
                }
              }
            }

            // --- 命名审核 ---
            if (!submission.hldp_v || submission.hldp_v !== '3.0') {
              result.naming = 'WARN';
              result.warnings.push('HLDP版本应为3.0');
            }

            if (!submission.msg_id || !submission.msg_id.startsWith('HLDP-')) {
              result.naming = 'FAIL';
              result.blockers.push('msg_id格式不正确·应以HLDP-开头');
            }

            if (payload.data && payload.data.submission_type) {
              const validTypes = ['dev_proposal', 'hotfix', 'dependency_update', 'architecture_change'];
              if (!validTypes.includes(payload.data.submission_type)) {
                result.naming = 'WARN';
                result.warnings.push(`未知的submission_type: ${payload.data.submission_type}`);
              }
            }

            // --- 综合判定 ---
            if (result.blockers.length > 0) {
              result.overall = 'REJECTED';
            } else if (result.warnings.length > 0) {
              result.overall = 'APPROVED';  // 有警告但仍可通过
            }

            // --- 额外建议 ---
            if (data.development_plan) {
              const plan = data.development_plan;
              if (!plan.steps || plan.steps.length === 0) {
                result.suggestions.push('建议补充具体的开发步骤');
              }
              if (plan.estimated_scope === 'large') {
                result.suggestions.push('大规模开发建议分阶段提交·每阶段独立审核');
              }
            }

            return result;
          }

          function generateReceipt(submission, review, tenant) {
            const now = new Date().toISOString();
            const receiptId = `HLDP-ZY-${now.slice(0,10).replace(/-/g,'')}-REC-${Date.now().toString(36).toUpperCase()}`;

            return {
              hldp_v: '3.0',
              msg_id: receiptId,
              msg_type: 'ack',
              sender: {
                id: 'ICE-GL-ZY001',
                name: '铸渊',
                role: 'guardian'
              },
              receiver: {
                id: tenant.tech_controller.persona_ice_id,
                name: tenant.tech_controller.persona_name
              },
              timestamp: now,
              priority: 'important',
              payload: {
                intent: '开发方案审核回执',
                data: {
                  ref_submission_id: submission.msg_id,
                  status: review.overall,
                  review_result: {
                    architecture: review.architecture,
                    security: review.security,
                    naming: review.naming,
                    overall: review.overall
                  },
                  guidance: {
                    suggestions: review.suggestions,
                    warnings: review.warnings,
                    blockers: review.blockers
                  },
                  auto_trigger: {
                    enabled: review.overall === 'APPROVED' && tenant.external_repo && tenant.external_repo.owner !== 'TODO',
                    target_repo: tenant.external_repo ? tenant.external_repo.repo : null,
                    target_owner: tenant.external_repo ? tenant.external_repo.owner : null,
                    trigger_type: 'repository_dispatch',
                    event_type: 'zhuyuan-dev-approved',
                    payload: {
                      submission_id: submission.msg_id,
                      approved_steps: review.overall === 'APPROVED'
                        ? (submission.payload?.data?.development_plan?.steps || []).map((_, i) => String(i + 1))
                        : [],
                      constraints: {
                        no_touch_files: ['.github/copilot-instructions.md', 'brain/'],
                        required_tests: true,
                        max_files_changed: 20
                      }
                    }
                  }
                },
                expected_response: 'ack'
              }
            };
          }

          async function downloadAndReview(submissionMeta) {
            return new Promise((resolve, reject) => {
              cos.getObject({
                Bucket: BUCKET,
                Region: REGION,
                Key: submissionMeta.key
              }, (err, data) => {
                if (err) {
                  console.error(`下载失败: ${submissionMeta.key}`, err.message);
                  resolve(null);
                  return;
                }

                try {
                  const submission = JSON.parse(data.Body.toString());
                  console.log(`\n审核: ${submission.msg_id || submissionMeta.key}`);
                  console.log(`  标题: ${submission.payload?.data?.title || '未知'}`);

                  const review = reviewSubmission(submission);
                  console.log(`  架构: ${review.architecture}`);
                  console.log(`  安全: ${review.security}`);
                  console.log(`  命名: ${review.naming}`);
                  console.log(`  结论: ${review.overall}`);

                  if (review.suggestions.length > 0) {
                    console.log(`  建议: ${review.suggestions.join('; ')}`);
                  }
                  if (review.warnings.length > 0) {
                    console.log(`  警告: ${review.warnings.join('; ')}`);
                  }
                  if (review.blockers.length > 0) {
                    console.log(`  阻塞: ${review.blockers.join('; ')}`);
                  }

                  const receipt = generateReceipt(submission, review, submissionMeta.tenant);
                  resolve({ submission, review, receipt, meta: submissionMeta });
                } catch (parseErr) {
                  console.error(`解析失败: ${submissionMeta.key}`, parseErr.message);
                  resolve(null);
                }
              });
            });
          }

          async function writeReceipt(receipt, tenant) {
            if (DRY_RUN) {
              console.log(`[DRY RUN] 跳过写入回执: ${receipt.msg_id}`);
              return true;
            }

            const receiptKey = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/${receipt.msg_id}.json`;

            return new Promise((resolve, reject) => {
              cos.putObject({
                Bucket: BUCKET,
                Region: REGION,
                Key: receiptKey,
                Body: JSON.stringify(receipt, null, 2)
              }, (err) => {
                if (err) {
                  console.error(`回执写入失败: ${receiptKey}`, err.message);
                  resolve(false);
                } else {
                  console.log(`回执已写入: ${receiptKey}`);
                  resolve(true);
                }
              });
            });
          }

          async function main() {
            const submissions = JSON.parse(fs.readFileSync('/tmp/new-submissions.json', 'utf8'));
            let reviewedCount = 0;
            let approvedCount = 0;
            let rejectedCount = 0;

            for (const sub of submissions) {
              const result = await downloadAndReview(sub);
              if (!result) continue;

              reviewedCount++;

              // 写回执到COS
              const written = await writeReceipt(result.receipt, sub.tenant);
              if (!written) continue;

              if (result.review.overall === 'APPROVED') {
                approvedCount++;
              } else {
                rejectedCount++;
              }

              // 保存回执到本地临时文件(用于后续dispatch步骤)
              fs.writeFileSync(
                `/tmp/receipt-${reviewedCount}.json`,
                JSON.stringify(result, null, 2)
              );
            }

            console.log(`\n========== 审核汇总 ==========`);
            console.log(`审核: ${reviewedCount}`);
            console.log(`通过: ${approvedCount}`);
            console.log(`拒绝: ${rejectedCount}`);

            fs.appendFileSync(process.env.GITHUB_OUTPUT, `reviewed=${reviewedCount}\n`);
            fs.appendFileSync(process.env.GITHUB_OUTPUT, `approved=${approvedCount}\n`);
            fs.appendFileSync(process.env.GITHUB_OUTPUT, `rejected=${rejectedCount}\n`);
          }

          main().catch(err => {
            console.error('审核流程失败:', err);
            process.exit(1);
          });
          REVIEW_SCRIPT

      - name: 🚀 触发对方仓库副驾驶 (APPROVED时)
        if: steps.review.outputs.approved > 0 && github.event.inputs.dry_run != 'true'
        env:
          DISPATCH_TOKEN: ${{ secrets.ZHUYUAN_DISPATCH_TOKEN }}
        run: |
          node << 'DISPATCH_SCRIPT'
          const fs = require('fs');
          const https = require('https');

          function sendDispatch(options, postData) {
            return new Promise((resolve) => {
              const req = https.request(options, (res) => {
                console.log(`  响应: ${res.statusCode}`);
                if (res.statusCode === 204) {
                  console.log(`  ✅ 副驾驶已触发`);
                } else {
                  let body = '';
                  res.on('data', chunk => body += chunk);
                  res.on('end', () => console.log(`  ⚠️ 响应: ${body}`));
                }
                resolve();
              });

              req.on('error', (e) => {
                console.error(`  ❌ 触发失败: ${e.message}`);
                resolve();
              });

              req.write(postData);
              req.end();
            });
          }

          async function main() {
          // 读取所有审核结果
          let i = 1;
          while (true) {
            const filePath = `/tmp/receipt-${i}.json`;
            if (!fs.existsSync(filePath)) break;

            const result = JSON.parse(fs.readFileSync(filePath, 'utf8'));
            const autoTrigger = result.receipt?.payload?.data?.auto_trigger;

            if (autoTrigger && autoTrigger.enabled && autoTrigger.target_owner && autoTrigger.target_repo) {
              const token = process.env.DISPATCH_TOKEN;
              if (!token) {
                console.log(`⚠️ ZHUYUAN_DISPATCH_TOKEN未配置·跳过dispatch`);
                i++;
                continue;
              }

              const postData = JSON.stringify({
                event_type: autoTrigger.event_type,
                client_payload: autoTrigger.payload
              });

              const options = {
                hostname: 'api.github.com',
                path: `/repos/${autoTrigger.target_owner}/${autoTrigger.target_repo}/dispatches`,
                method: 'POST',
                headers: {
                  'Authorization': `Bearer ${token}`,
                  'Accept': 'application/vnd.github.v3+json',
                  'Content-Type': 'application/json',
                  'User-Agent': 'Zhuyuan-Dev-Review-Bridge/1.0',
                  'Content-Length': Buffer.byteLength(postData)
                }
              };

              console.log(`触发: ${autoTrigger.target_owner}/${autoTrigger.target_repo}`);
              console.log(`  事件: ${autoTrigger.event_type}`);
              console.log(`  提交ID: ${autoTrigger.payload.submission_id}`);

              await sendDispatch(options, postData);

              // 请求间隔
              await new Promise(resolve => setTimeout(resolve, 2000));
            }

            i++;
          }
          }

          main().catch(err => {
            console.error('触发流程失败:', err);
            process.exit(1);
          });
          DISPATCH_SCRIPT

      - name: 📊 审核报告
        if: always()
        run: |
          echo "============================================"
          echo "COS开发审核桥接 · 完成"
          echo "时间: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
          echo "新提交: ${{ steps.scan.outputs.count || '0' }}"
          echo "已审核: ${{ steps.review.outputs.reviewed || '0' }}"
          echo "通过: ${{ steps.review.outputs.approved || '0' }}"
          echo "拒绝: ${{ steps.review.outputs.rejected || '0' }}"
          echo "DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}"
          echo "============================================"
          echo ""
          echo "铸渊 · ICE-GL-ZY001 · 审核桥接守护完毕"
