/**
 * DataScope Resource 命名合规检查（规则 §5.3.1.3 + §5.3.17）
 *
 * 扫描：
 * 1. 所有 @DataScope('xxx') 装饰器调用里的 resource 值
 * 2. seed 文件里 RoleDataScope.resource 值
 *
 * 校验：
 * - 必须在 backend/src/common/constants/data-scope-resources.ts 的 KNOWN_DATA_SCOPE_RESOURCES 集合里
 * - 命名必须是 snake_case 名词单数（无大写、无 pluralS、无横线）
 *
 * 用法：
 *   npx ts-node --transpile-only --project testing/scripts/tsconfig.json testing/scripts/data-scope-resources-check.ts
 *
 * 挂到 pre-commit：文件改到 .prisma 或 @DataScope 相关时触发
 */

import * as fs from 'fs';
import * as path from 'path';
import { execSync } from 'child_process';

const ROOT_DIR = path.resolve(__dirname, '..', '..');

function loadKnownResources(): Set<string> {
  const constFile = path.join(
    ROOT_DIR,
    'backend/src/common/constants/data-scope-resources.ts',
  );
  if (!fs.existsSync(constFile)) {
    console.error(`❌ 找不到常量文件：${constFile}`);
    process.exit(1);
  }
  const content = fs.readFileSync(constFile, 'utf-8');
  // 提取所有形如 `KEY: 'value',`（DATA_SCOPE_RESOURCE 主常量）以及
  // LEGACY_DATA_SCOPE_RESOURCES new Set 里的 'value' 字符串。
  const values = new Set<string>();
  const constRe = /^\s*[A-Z_]+:\s*'([^']+)',?/gm;
  let m: RegExpExecArray | null;
  while ((m = constRe.exec(content)) !== null) {
    values.add(m[1]);
  }
  const legacyMatch = content.match(
    /LEGACY_DATA_SCOPE_RESOURCES[^=]*=\s*new Set\(\s*\[([^\]]*)\]/,
  );
  if (legacyMatch) {
    const legacyRe = /'([^']+)'/g;
    while ((m = legacyRe.exec(legacyMatch[1])) !== null) {
      values.add(m[1]);
    }
  }
  return values;
}

function findDecoratorUsages(): Array<{ file: string; line: number; resource: string }> {
  const results: Array<{ file: string; line: number; resource: string }> = [];
  try {
    const output = execSync(
      `grep -rn "@DataScope(" backend/src --include='*.ts' || true`,
      { cwd: ROOT_DIR, encoding: 'utf-8' },
    );
    for (const raw of output.split('\n')) {
      if (!raw) continue;
      const m = raw.match(/^([^:]+):(\d+):.*@DataScope\(\s*['"]([^'"]+)['"]/);
      if (m) {
        results.push({ file: m[1], line: parseInt(m[2], 10), resource: m[3] });
      }
    }
  } catch {
    // noop
  }
  return results;
}

function findSeedUsages(): Array<{ file: string; line: number; resource: string }> {
  const results: Array<{ file: string; line: number; resource: string }> = [];
  try {
    const output = execSync(
      `grep -rn "resource:\\s*['\\\"]" backend/prisma/seeds --include='*.ts' || true`,
      { cwd: ROOT_DIR, encoding: 'utf-8' },
    );
    for (const raw of output.split('\n')) {
      if (!raw) continue;
      const m = raw.match(/^([^:]+):(\d+):.*resource:\s*['"]([^'"]+)['"]/);
      if (m) {
        results.push({ file: m[1], line: parseInt(m[2], 10), resource: m[3] });
      }
    }
  } catch {
    // noop
  }
  return results;
}

// 历史命名豁免来源同 backend 常量（详见 docs/todos/iam-data-scope-naming-debt.md）。
// 本脚本独立运行（无 module resolution 到 backend/src），用裸字符串镜像该集合。
const LEGACY_NAMING_EXEMPT = new Set<string>([
  'org',
  'parts',
  'site-attendance',
  'robot-manager',
]);

function isValidNaming(value: string): boolean {
  if (value === '*') return true; // 通配符合法
  if (LEGACY_NAMING_EXEMPT.has(value)) return true; // 历史命名豁免
  // snake_case 名词单数：小写 + 下划线；不允许结尾 s（除非单数本身带 s，但项目约定单数）
  if (!/^[a-z][a-z_]*$/.test(value)) return false;
  return true;
}

function main() {
  const known = loadKnownResources();

  const decoratorUsages = findDecoratorUsages();
  const seedUsages = findSeedUsages();

  const errors: string[] = [];

  for (const u of [...decoratorUsages, ...seedUsages]) {
    if (!known.has(u.resource)) {
      errors.push(
        `${u.file}:${u.line} resource "${u.resource}" 不在 data-scope-resources.ts 常量里`,
      );
    }
    if (!isValidNaming(u.resource)) {
      errors.push(
        `${u.file}:${u.line} resource "${u.resource}" 不符合命名规范（snake_case 名词单数）`,
      );
    }
  }

  if (errors.length > 0) {
    console.error('\n❌ DataScope Resource 命名合规检查失败\n');
    errors.forEach((e) => console.error(`  ${e}`));
    console.error(
      `\n规则详见 docs/standards/09-iam-security.md §5.3.1.3`,
    );
    console.error(
      `修复：在 backend/src/common/constants/data-scope-resources.ts 追加常量，或修正拼写\n`,
    );
    process.exit(1);
  }

  console.log(
    `✅ DataScope Resource 检查通过：装饰器 ${decoratorUsages.length} 处 + seed ${seedUsages.length} 处，全部合规`,
  );
}

main();
