/**
 * L1c 数据质量校验脚本（通用版）
 *
 * 结构：
 * 1. 通用检查（所有模块共享）：权限种子、组织根部门、用户角色、DB schema FK/枚举
 * 2. 模块专属检查：从 module-config/{module}.quality.ts 加载
 *
 * 运行方式:
 *   npx ts-node --transpile-only testing/scripts/data-quality-check.ts --module performance
 *   npx ts-node --transpile-only testing/scripts/data-quality-check.ts --module asset-management
 *   npx ts-node --transpile-only testing/scripts/data-quality-check.ts                          # 默认 performance
 *
 * 依赖: DATABASE_URL 必须指向待校验的 DB（默认 psql shell-out）。
 *       如需自定义连接命令（如 SSH 隧道、docker exec），可设 DB_CMD 完全覆盖默认。
 *       数据库不可达时启动期 connectivity probe 立即失败，不会沉默假阳性。
 */

const { execSync } = require('child_process') as typeof import('child_process');
const { parseModuleArg, loadModuleConfig } = require('./module-config/resolve-module');

const MODULE_NAME = parseModuleArg('performance');

// ============================================================================
// 数据源安全护栏（按"禁止直连生产 DB"规则）
// ============================================================================
// 必须显式声明 DATA_SOURCE，禁止脚本悄无声息连到任意 DB：
//   - seed     ：纯 seed 数据（默认 dev container）
//   - dev      ：本地 dev DB（dev 累积数据）
//   - uat      ：UAT 验收库（仅 deploy-uat post-deploy 跑）
//   - snapshot ：生产脱敏快照（独立 CI 任务，临时 DB）
//   - prod-ro  ：生产 read-only 监控视图（仅 L4 持续监控；禁止直连生产主库）
//
// DB_CMD 中若包含已知的生产 IP/Host 但 DATA_SOURCE ≠ prod-ro，立即报错退出。
// ============================================================================
const PROD_HOST_PATTERNS = [
  '43.130.6.44',           // production server
  /\bproduction\b/i,
  /\bffws_prod\b/,
];
const ALLOWED_DATA_SOURCES = ['seed', 'dev', 'uat', 'snapshot', 'prod-ro'] as const;
type DataSource = typeof ALLOWED_DATA_SOURCES[number];

const DATA_SOURCE = (process.env.DATA_SOURCE || 'seed') as DataSource;
if (!ALLOWED_DATA_SOURCES.includes(DATA_SOURCE)) {
  console.error(`❌ DATA_SOURCE='${DATA_SOURCE}' 非法，必须是: ${ALLOWED_DATA_SOURCES.join(' | ')}`);
  process.exit(2);
}

// DB 连接源：DATABASE_URL（推荐，每环境标准）+ DB_CMD（高级覆盖）
// 默认走 psql 直连 DATABASE_URL，psql 必须在 PATH。CI 上 ubuntu runner 默认带。
const DATABASE_URL = process.env.DATABASE_URL;
const DB_CMD = process.env.DB_CMD
  || (DATABASE_URL ? `psql "${DATABASE_URL}" -t -A -c` : '');

if (!DB_CMD) {
  console.error(`❌ 必须设置 DATABASE_URL（推荐）或 DB_CMD（高级）`);
  console.error(`   DATA_SOURCE=${DATA_SOURCE} 但没有可用的 DB 连接方式`);
  process.exit(2);
}

// 拦截"DB_CMD/DATABASE_URL 指向生产 host 但 DATA_SOURCE 不是 prod-ro"的危险组合
const probeForProd = `${DB_CMD} ${DATABASE_URL ?? ''}`;
const looksLikeProd = PROD_HOST_PATTERNS.some(p =>
  typeof p === 'string' ? probeForProd.includes(p) : p.test(probeForProd)
);
if (looksLikeProd && DATA_SOURCE !== 'prod-ro') {
  console.error(`❌ 连接配置指向疑似生产环境，但 DATA_SOURCE='${DATA_SOURCE}'`);
  console.error(`   生产数据只允许通过 read-only 视图访问，且必须 DATA_SOURCE=prod-ro 显式声明`);
  process.exit(2);
}
if (DATA_SOURCE === 'prod-ro') {
  console.warn(`⚠️  DATA_SOURCE=prod-ro：连接生产 read-only 视图，本脚本只读不会写入，但请确认连接的确是 read-only`);
}
console.log(`[L1c] DATA_SOURCE=${DATA_SOURCE}, MODULE=${MODULE_NAME}`);

// 出错抛异常而不是吞掉——区分"无数据"（返回 ''）和"连接失败"（throw）
function query(sql: string): string {
  return execSync(`${DB_CMD} "${sql.replace(/"/g, '\\"')}"`, {
    encoding: 'utf-8',
    stdio: ['ignore', 'pipe', 'pipe'],
  }).trim();
}

// 启动期连接性自检——避免假阳性："连不上 DB" 跟"DB 里数据缺失"是两件事
try {
  const probe = query("SELECT 1");
  if (probe !== '1') {
    throw new Error(`SELECT 1 returned: ${JSON.stringify(probe)}`);
  }
} catch (e) {
  console.error(`❌ DB connectivity probe failed. 后续所有 check 都会是假阳性，提前终止。`);
  console.error(`   原因: ${(e as Error).message}`);
  console.error(`   DB_CMD: ${DB_CMD}`);
  console.error(`   DATABASE_URL set: ${DATABASE_URL ? 'yes' : 'no'}`);
  process.exit(2);
}

function queryRows(sql: string): string[] {
  const result = query(sql);
  return result ? result.split('\n').filter(Boolean) : [];
}

interface CheckResult {
  check: string;
  status: 'pass' | 'fail' | 'warn';
  detail: string;
}

const results: CheckResult[] = [];

function check(name: string, fn: () => { status: 'pass' | 'fail' | 'warn'; detail: string }) {
  try {
    const result = fn();
    results.push({ check: name, ...result });
  } catch (e) {
    results.push({ check: name, status: 'fail', detail: `异常: ${(e as Error).message}` });
  }
}

console.log('=== L1c 数据质量校验报告 ===');
console.log(`检查时间: ${new Date().toISOString().split('T')[0]}`);
console.log(`模块: ${MODULE_NAME}`);
console.log('');

// ==================== 通用检查（所有模块共享） ====================

console.log('--- 通用检查 ---\n');

// 1. 权限种子检查（⛔ 阻断级）
check(`[${MODULE_NAME}] 权限种子已存在`, () => {
  const permPrefix = MODULE_NAME; // 如 'performance', 'asset-management'
  const rows = queryRows(
    `SELECT concat(resource, ':', action) AS code FROM platform_iam.permissions WHERE resource = '${permPrefix}' OR resource LIKE '${permPrefix}:%' ORDER BY resource, action`
  );
  if (rows.length === 0) {
    return { status: 'fail', detail: `permissions 表中无 '${permPrefix}:%' 权限码。非管理员用户将无法使用模块。` };
  }
  return { status: 'pass', detail: `${rows.length} 个权限码` };
});

// 2. 组织必须有同名根部门
check('每个组织都有根部门（parent_id IS NULL）', () => {
  const orgsWithoutRoot = queryRows(
    `SELECT o.id, o.name FROM corp_hr.organizations o
     LEFT JOIN corp_hr.departments d ON d.organization_id = o.id AND d.parent_id IS NULL AND d.deleted_at IS NULL
     WHERE o.deleted_at IS NULL AND o.status = 'ACTIVE' AND d.id IS NULL`
  );
  if (orgsWithoutRoot.length > 0) {
    return { status: 'fail', detail: `${orgsWithoutRoot.length} 个组织无根部门: ${orgsWithoutRoot.map(r => r.split('|')[1]).join(', ')}` };
  }
  return { status: 'pass', detail: '所有活跃组织都有根部门' };
});

// 3. 用户角色分配完整性
check('所有用户都有至少一个角色', () => {
  const usersWithoutRole = queryRows(
    `SELECT u.id, u.username FROM platform_iam.users u
     LEFT JOIN platform_iam.user_role_rel ur ON ur.user_id = u.id
     WHERE u.deleted_at IS NULL AND u.is_active = true AND ur.id IS NULL`
  );
  if (usersWithoutRole.length > 0) {
    return { status: 'warn', detail: `${usersWithoutRole.length} 个活跃用户无角色: ${usersWithoutRole.slice(0, 5).map(r => r.split('|')[1]).join(', ')}${usersWithoutRole.length > 5 ? '...' : ''}` };
  }
  return { status: 'pass', detail: '所有活跃用户都有角色' };
});

// 4. 自动检测模块 DB schema 的 FK 约束覆盖
check(`[${MODULE_NAME}] DB schema FK 约束`, () => {
  // 推测 schema 名称：module-name → 蛇形命名
  const possibleSchemas = [
    MODULE_NAME.replace(/-/g, '_'),                    // asset_management
    `platform_${MODULE_NAME.replace(/-/g, '_')}`,      // platform_performance
  ];

  for (const schema of possibleSchemas) {
    const fkCount = query(
      `SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY' AND table_schema = '${schema}'`
    );
    if (fkCount && parseInt(fkCount) > 0) {
      const tableCount = query(
        `SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${schema}'`
      );
      return { status: 'pass', detail: `schema '${schema}': ${tableCount} 张表，${fkCount} 条 FK 约束` };
    }
  }

  // 尝试 asset_mgmt 等缩写形式
  const shortSchemas = queryRows(
    `SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('public','information_schema','pg_catalog','pg_toast') ORDER BY schema_name`
  );
  const moduleKeyword = MODULE_NAME.replace(/-/g, '_').replace(/_management$/, '');
  const matched = shortSchemas.find(s => s.includes(moduleKeyword));
  if (matched) {
    const fkCount = query(
      `SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY' AND table_schema = '${matched}'`
    );
    const tableCount = query(
      `SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${matched}'`
    );
    return { status: 'pass', detail: `schema '${matched}': ${tableCount} 张表，${fkCount} 条 FK 约束` };
  }

  return { status: 'warn', detail: `未找到匹配的 DB schema（尝试了: ${possibleSchemas.join(', ')}）` };
});

// ==================== 模块专属检查（从配置文件加载） ====================

const qualityConfig = loadModuleConfig(MODULE_NAME, 'quality');
if (qualityConfig?.checks && Array.isArray(qualityConfig.checks)) {
  console.log(`--- ${MODULE_NAME} 模块专属检查 (${qualityConfig.checks.length} 项) ---\n`);
  for (const moduleCheck of qualityConfig.checks) {
    check(moduleCheck.name, () => moduleCheck.fn(query, queryRows));
  }
} else {
  console.log(`--- 无 ${MODULE_NAME} 模块专属检查（未找到 module-config/${MODULE_NAME}.quality.ts） ---\n`);
}

// ==================== 输出报告 ====================

console.log('============================================================');
console.log('                    校验结果');
console.log('============================================================');

let hasFailure = false;
for (const r of results) {
  const icon = r.status === 'pass' ? '✅' : r.status === 'fail' ? '❌' : '⚠️';
  if (r.status === 'fail') hasFailure = true;
  console.log(`${icon} ${r.check}`);
  if (r.status !== 'pass') {
    console.log(`  ${r.detail}`);
  }
}

console.log('');
console.log('============================================================');
console.log(`  通过: ${results.filter(r => r.status === 'pass').length}`);
console.log(`  失败: ${results.filter(r => r.status === 'fail').length}`);
console.log(`  警告: ${results.filter(r => r.status === 'warn').length}`);
console.log('============================================================');

process.exit(hasFailure ? 1 : 0);
