/**
 * L0c 响应快照对比脚本（通用版）
 *
 * 自动从前端 API 文件中提取所有 GET API 端点，
 * 发真实 HTTP 请求，提取返回 JSON 的字段路径，与前端 interface 自动对比。
 *
 * 运行方式:
 *   npx ts-node --project testing/scripts/tsconfig.json testing/scripts/response-snapshot-check.ts --module performance
 *   npx ts-node --project testing/scripts/tsconfig.json testing/scripts/response-snapshot-check.ts --module asset-management
 */

import * as http from 'http';
import * as fs from 'fs';
import * as path from 'path';

const { parseModuleArg, resolveModulePaths, loadModuleConfig } = require('./module-config/resolve-module');

// ==================== 配置（从 --module 参数自动推导） ====================

const MODULE_NAME = parseModuleArg('performance');
const MODULE = resolveModulePaths(MODULE_NAME);

const API_BASE = process.env.API_BASE_URL || 'http://localhost:3001/api/v1';
const LOGIN_URL = `${API_BASE}/auth/login`;
const MODULE_BASE = `${API_BASE}${MODULE.apiPrefix}`;
const TIMEOUT_MS = 5000;

const LOGIN_CREDENTIALS = {
  username: 'itadmin',
  password: 'Admin@2024',
};

const FRONTEND_API_FILE = MODULE.frontendApiFile;

// ==================== HTTP 工具 ====================

interface HttpResponse {
  statusCode: number;
  body: any;
  raw: string;
}

function httpRequest(
  url: string,
  options: { method?: string; headers?: Record<string, string>; body?: string },
): Promise<HttpResponse> {
  return new Promise((resolve, reject) => {
    const parsed = new URL(url);
    const req = http.request(
      {
        hostname: parsed.hostname,
        port: parsed.port || 80,
        path: parsed.pathname + parsed.search,
        method: options.method || 'GET',
        headers: {
          'Content-Type': 'application/json',
          ...options.headers,
        },
        timeout: TIMEOUT_MS,
      },
      (res) => {
        const chunks: Buffer[] = [];
        res.on('data', (chunk) => chunks.push(chunk));
        res.on('end', () => {
          const raw = Buffer.concat(chunks).toString();
          let body: any;
          try {
            body = JSON.parse(raw);
          } catch {
            body = raw;
          }
          resolve({ statusCode: res.statusCode || 0, body, raw });
        });
      },
    );
    req.on('error', (err) => reject(err));
    req.on('timeout', () => {
      req.destroy();
      reject(new Error(`请求超时: ${url}`));
    });
    if (options.body) req.write(options.body);
    req.end();
  });
}

// ==================== 字段路径提取 ====================

/**
 * 提取顶层字段名（不含嵌套路径）
 */
function extractTopLevelKeys(obj: any): string[] {
  if (obj === null || obj === undefined || typeof obj !== 'object' || Array.isArray(obj)) return [];
  return Object.keys(obj);
}

// ==================== 自动端点提取 ====================

interface ExtractedEndpoint {
  /** 函数名，如 getCycles */
  functionName: string;
  /** 相对路径，如 /cycles, /cycles/:id/statistics */
  urlPath: string;
  /** 原始 URL 模板，如 `${BASE_URL}/cycles/${id}` */
  rawUrlTemplate: string;
  /** URL 中的模板变量名列表，如 ['id', 'cycleId'] */
  templateVars: string[];
  /** 查询参数名列表（从 { params: { ... } } 提取） */
  queryParams: string[];
  /** 函数参数名列表 */
  funcArgs: string[];
  /** 返回类型字符串 */
  returnType: string;
}

/**
 * 从前端 performance.ts 自动提取所有 GET 类型的 API 端点
 */
function extractGetEndpoints(filePath: string): ExtractedEndpoint[] {
  const content = fs.readFileSync(filePath, 'utf-8');
  const endpoints: ExtractedEndpoint[] = [];

  // 按函数分割。匹配 export async function xxx(...): Promise<...> { ... }
  // 用一个策略：找到每个 export async function，然后向后找到对应的 apiClient.get 调用
  const lines = content.split('\n');

  let i = 0;
  while (i < lines.length) {
    // 匹配函数签名行
    const funcMatch = lines[i].match(
      /^export\s+async\s+function\s+(\w+)\s*\((.*)/,
    );
    if (!funcMatch) {
      i++;
      continue;
    }

    const functionName = funcMatch[1];

    // 收集函数完整签名（可能跨行）和函数体
    let sigAndBody = '';
    let braceCount = 0;
    let foundOpenBrace = false;
    for (let j = i; j < lines.length; j++) {
      sigAndBody += lines[j] + '\n';
      for (const ch of lines[j]) {
        if (ch === '{') {
          braceCount++;
          foundOpenBrace = true;
        }
        if (ch === '}') braceCount--;
      }
      if (foundOpenBrace && braceCount === 0) {
        i = j + 1;
        break;
      }
    }

    if (!foundOpenBrace) {
      i++;
      continue;
    }

    // 检查函数体中是否有 apiClient.get 调用（排除 demo 分支中的 early return）
    // 策略：找到非 demo 分支中的 apiClient.get
    // 注意：泛型参数可能含嵌套尖括号，如 <any, { items: GradeConfig[] }>
    const getCallMatch = sigAndBody.match(
      /(?:return\s+)?(?:const\s+\w+\s*=\s*)?(?:await\s+)?apiClient\.get(?:<[^>]*(?:<[^>]*>[^>]*)?>)?\s*\(\s*([`'][^`']*[`'])/,
    );
    if (!getCallMatch) {
      continue;
    }

    const rawUrlTemplate = getCallMatch[1];

    // 将 URL 模板转为路径：替换 ${BASE_URL} -> /performance, 去掉反引号/引号
    let urlPath = rawUrlTemplate.replace(/[`']/g, '');
    urlPath = urlPath.replace(/\$\{BASE_URL\}/g, '');

    // 处理没有 BASE_URL 的情况（如 '/{module}/sub-path'）
    if (urlPath.startsWith(MODULE.apiPrefix + '/')) {
      urlPath = urlPath.substring(MODULE.apiPrefix.length);
    } else if (urlPath.startsWith(MODULE.apiPrefix)) {
      urlPath = urlPath.substring(MODULE.apiPrefix.length) || '/';
    }

    // 提取模板变量：${id}, ${cycleId} 等
    const templateVars: string[] = [];
    const varRegex = /\$\{(\w+)\}/g;
    let varMatch: RegExpExecArray | null;
    // 先去掉 ${BASE_URL}
    const urlWithoutBase = rawUrlTemplate.replace(/\$\{BASE_URL\}/g, '');
    while ((varMatch = varRegex.exec(urlWithoutBase)) !== null) {
      templateVars.push(varMatch[1]);
    }

    // 将 URL 中的 ${xxx} 替换为 :xxx 风格
    urlPath = urlPath.replace(/\$\{(\w+)\}/g, ':$1');

    // 提取查询参数：从 { params: { cycleId, employeeId } } 或 { params }
    const queryParams: string[] = [];
    const paramsMatch = sigAndBody.match(
      /apiClient\.get[^(]*\([^,]+,\s*\{\s*params:\s*\{([^}]*)\}/,
    );
    if (paramsMatch) {
      const paramsStr = paramsMatch[1];
      // 提取参数名（可能是 cycleId, employeeId 或 key: value 形式）
      const paramNames = paramsStr.split(',').map((p) => {
        const trimmed = p.trim();
        // 处理 key: value 形式
        const colonIdx = trimmed.indexOf(':');
        if (colonIdx > 0) return trimmed.substring(0, colonIdx).trim();
        return trimmed;
      }).filter(Boolean);
      queryParams.push(...paramNames);
    } else {
      // 检查 { params } 形式（传递整个 params 对象）
      const simpleParamsMatch = sigAndBody.match(
        /apiClient\.get[^(]*\([^,]+,\s*\{\s*params\s*\}/,
      );
      if (simpleParamsMatch) {
        // params 是从函数参数传入的，标记为动态
        queryParams.push('_dynamic_params');
      }
    }

    // 提取函数参数
    const funcArgs: string[] = [];
    const sigMatch = sigAndBody.match(
      /^export\s+async\s+function\s+\w+\s*\(([^)]*)\)/m,
    );
    if (sigMatch) {
      const argsStr = sigMatch[1];
      const args = argsStr.split(',').map((a) => {
        const trimmed = a.trim();
        const nameMatch = trimmed.match(/^(\w+)/);
        return nameMatch ? nameMatch[1] : '';
      }).filter(Boolean);
      funcArgs.push(...args);
    }

    // 提取返回类型
    let returnType = '';
    const retMatch = sigAndBody.match(/\):\s*Promise<([^{]*)>\s*\{/);
    if (retMatch) {
      returnType = retMatch[1].trim();
    }

    // 跳过重复端点（同一 URL 可能有别名函数，如 getEvaluations360 和 getEvaluation360s）
    const isDuplicate = endpoints.some((ep) => ep.urlPath === urlPath && ep.functionName !== functionName);
    if (isDuplicate) {
      continue;
    }

    endpoints.push({
      functionName,
      urlPath,
      rawUrlTemplate,
      templateVars,
      queryParams,
      funcArgs,
      returnType,
    });
  }

  return endpoints;
}

// ==================== 手动增强配置（可选） ====================

/**
 * 可选的手动增强配置。
 * key 为函数名，可以指定：
 * - frontendFields: 期望的一级字段列表（用于严格对比）
 * - nestedFields: 嵌套字段映射
 * - skip: 是否跳过该端点
 * - extraQueryParams: 额外查询参数
 */
interface EndpointOverride {
  frontendFields?: string[];
  nestedFields?: Record<string, string[]>;
  skip?: boolean;
  extraQueryParams?: Record<string, string>;
}

// 从模块配置加载端点覆盖，无配置时为空
const snapshotConfig = loadModuleConfig(MODULE_NAME, 'snapshot');
const ENDPOINT_OVERRIDES: Record<string, EndpointOverride> = snapshotConfig?.endpointOverrides || {};

// ==================== 对比逻辑 ====================

interface NestedCheckResult {
  fieldName: string;
  status: 'match' | 'mismatch' | 'empty' | 'not_array';
  backendKeys?: string[];
  frontendKeys?: string[];
  missing?: string[];   // 前端期望但后端没返回
  extra?: string[];     // 后端返回但前端未定义
}

type CheckResult =
  | { status: 'match'; endpoint: string; backendCount: number; frontendCount: number; nestedResults?: NestedCheckResult[] }
  | { status: 'mismatch'; endpoint: string; backendOnly: string[]; frontendOnly: string[]; details: string; nestedResults?: NestedCheckResult[] }
  | { status: 'backend_missing'; endpoint: string; frontendOnly: string[]; nestedResults?: NestedCheckResult[] }
  | { status: 'info'; endpoint: string; backendFields: string[]; message: string }
  | { status: 'skipped'; endpoint: string; reason: string }
  | { status: 'error'; endpoint: string; reason: string };

/**
 * 对比后端返回字段与前端 interface 字段
 */
function compareFields(
  backendKeys: string[],
  frontendFields: string[],
  endpointName: string,
): CheckResult {
  const backendSet = new Set(backendKeys);
  const frontendSet = new Set(frontendFields);

  const backendOnly = backendKeys.filter((k) => !frontendSet.has(k));
  const frontendOnly = frontendFields.filter((k) => !backendSet.has(k));

  if (backendOnly.length === 0 && frontendOnly.length === 0) {
    return {
      status: 'match',
      endpoint: endpointName,
      backendCount: backendKeys.length,
      frontendCount: frontendFields.length,
    };
  }

  // 前端有但后端没有 = 前端可能拿到 undefined
  if (frontendOnly.length > 0 && backendOnly.length === 0) {
    return {
      status: 'backend_missing',
      endpoint: endpointName,
      frontendOnly,
    };
  }

  const details: string[] = [];
  if (backendOnly.length > 0) {
    details.push(`后端返回但前端未定义: ${backendOnly.join(', ')}`);
  }
  if (frontendOnly.length > 0) {
    details.push(`前端期望但后端未返回: ${frontendOnly.join(', ')}`);
  }

  return {
    status: 'mismatch',
    endpoint: endpointName,
    backendOnly,
    frontendOnly,
    details: details.join('\n     '),
  };
}

/**
 * 对嵌套字段进行对比：取数组的第一个元素或对象本身，提取字段名
 */
function checkNestedFields(
  data: any,
  nestedFields: Record<string, string[]>,
): NestedCheckResult[] {
  const results: NestedCheckResult[] = [];

  for (const [fieldName, expectedKeys] of Object.entries(nestedFields)) {
    const fieldValue = data?.[fieldName];

    if (fieldValue === null || fieldValue === undefined) {
      results.push({ fieldName, status: 'empty' });
      continue;
    }

    let target: any;
    if (Array.isArray(fieldValue)) {
      if (fieldValue.length === 0) {
        results.push({ fieldName, status: 'empty' });
        continue;
      }
      target = fieldValue[0];
    } else if (typeof fieldValue === 'object') {
      target = fieldValue;
    } else {
      results.push({ fieldName, status: 'not_array' });
      continue;
    }

    if (target === null || target === undefined || typeof target !== 'object') {
      results.push({ fieldName, status: 'not_array' });
      continue;
    }

    const backendKeys = Object.keys(target);
    const backendSet = new Set(backendKeys);
    const frontendSet = new Set(expectedKeys);

    const missing = expectedKeys.filter((k) => !backendSet.has(k));
    const extra = backendKeys.filter((k) => !frontendSet.has(k));

    if (missing.length === 0 && extra.length === 0) {
      results.push({ fieldName, status: 'match', backendKeys, frontendKeys: expectedKeys });
    } else {
      results.push({ fieldName, status: 'mismatch', backendKeys, frontendKeys: expectedKeys, missing, extra });
    }
  }

  return results;
}

// ==================== URL 构建辅助 ====================

/**
 * 根据提取的端点信息和已知参数值，构建完整请求 URL
 */
function buildUrl(
  ep: ExtractedEndpoint,
  knownParams: Record<string, string>,
): { url: string; skippedReason?: string } {
  let urlPath = ep.urlPath;

  // 替换路径中的 :xxx 变量
  for (const varName of ep.templateVars) {
    const placeholder = `:${varName}`;
    if (!urlPath.includes(placeholder)) continue;

    // 尝试从已知参数匹配
    const value = resolveParamValue(varName, knownParams);
    if (!value) {
      return { url: '', skippedReason: `缺少路径参数: ${varName}` };
    }
    urlPath = urlPath.replace(placeholder, value);
  }

  let url = `${MODULE_BASE}${urlPath}`;
  const queryParts: string[] = [];

  // 添加查询参数
  for (const paramName of ep.queryParams) {
    if (paramName === '_dynamic_params') continue; // 动态 params，无法自动填充
    const value = resolveParamValue(paramName, knownParams);
    if (value) {
      queryParts.push(`${paramName}=${encodeURIComponent(value)}`);
    }
  }

  // 对于需要 cycleId 作为查询参数的（路径中没有 cycleId 但函数参数有）
  if (
    ep.funcArgs.includes('cycleId') &&
    !ep.templateVars.includes('cycleId') &&
    !ep.queryParams.includes('cycleId') &&
    knownParams.cycleId
  ) {
    queryParts.push(`cycleId=${knownParams.cycleId}`);
  }

  // 对于需要 employeeId 的
  if (
    ep.funcArgs.includes('employeeId') &&
    !ep.templateVars.includes('employeeId') &&
    !ep.queryParams.includes('employeeId') &&
    knownParams.employeeId
  ) {
    queryParts.push(`employeeId=${knownParams.employeeId}`);
  }

  // 添加 override 中的额外参数（含模块专属的额外查询参数）
  const override = ENDPOINT_OVERRIDES[ep.functionName];
  if (override?.extraQueryParams) {
    for (const [k, v] of Object.entries(override.extraQueryParams)) {
      queryParts.push(`${k}=${encodeURIComponent(v)}`);
    }
  }

  if (queryParts.length > 0) {
    const sep = url.includes('?') ? '&' : '?';
    url = url + sep + queryParts.join('&');
  }

  return { url };
}

/**
 * 从已知参数中解析值
 */
function resolveParamValue(
  paramName: string,
  knownParams: Record<string, string>,
): string | undefined {
  // 直接匹配
  if (knownParams[paramName]) return knownParams[paramName];

  // 常见别名映射
  const aliases: Record<string, string[]> = {
    id: ['cycleId', 'genericId'],
    cycleId: ['cycleId'],
    employeeId: ['employeeId', 'currentUserId'],
    assignmentId: ['assignmentId'],
    sessionId: ['sessionId'],
  };

  const candidates = aliases[paramName] || [];
  for (const alias of candidates) {
    if (knownParams[alias]) return knownParams[alias];
  }

  return undefined;
}

/**
 * 从响应数据中智能提取要对比的数据对象
 * 处理后端 { success, data } 包装、分页结构、数组等
 */
function extractResponseData(body: any): {
  data: any;
  isPaginated: boolean;
  isArray: boolean;
  paginationKeys?: string[];
} {
  // 解包 { success: true, data: ... }
  let responseData = (body?.success === true && 'data' in body) ? body.data : body;

  // 检查是否为分页结构
  if (
    responseData &&
    typeof responseData === 'object' &&
    !Array.isArray(responseData) &&
    'items' in responseData &&
    ('total' in responseData || 'totalPages' in responseData)
  ) {
    return {
      data: responseData,
      isPaginated: true,
      isArray: false,
      paginationKeys: Object.keys(responseData),
    };
  }

  // 检查是否为数组
  if (Array.isArray(responseData)) {
    return { data: responseData, isPaginated: false, isArray: true };
  }

  // 检查是否为 { items: [...] } 包装（非标准分页）
  if (
    responseData &&
    typeof responseData === 'object' &&
    'items' in responseData &&
    Array.isArray(responseData.items)
  ) {
    return {
      data: responseData,
      isPaginated: false,
      isArray: false,
    };
  }

  return { data: responseData, isPaginated: false, isArray: false };
}

// ==================== 主流程 ====================

async function main() {
  console.log('=== L0c 响应快照对比报告（自动提取模式） ===\n');
  console.log(`时间: ${new Date().toISOString()}`);
  console.log(`目标: ${API_BASE}`);
  console.log(`前端源: ${FRONTEND_API_FILE}\n`);

  // ── 0. 从前端代码自动提取 GET 端点 ──
  if (!fs.existsSync(FRONTEND_API_FILE)) {
    console.error(`❌ 前端 API 文件不存在: ${FRONTEND_API_FILE}`);
    process.exit(1);
  }

  const extractedEndpoints = extractGetEndpoints(FRONTEND_API_FILE);
  console.log(`📦 从前端代码提取到 ${extractedEndpoints.length} 个 GET 端点:\n`);
  for (const ep of extractedEndpoints) {
    const vars = ep.templateVars.length > 0 ? ` (路径变量: ${ep.templateVars.join(', ')})` : '';
    const params = ep.queryParams.filter(p => p !== '_dynamic_params').length > 0
      ? ` (查询参数: ${ep.queryParams.filter(p => p !== '_dynamic_params').join(', ')})`
      : '';
    console.log(`  ${ep.functionName} → ${ep.urlPath}${vars}${params}`);
  }
  console.log('');

  // ── 1. 登录获取 token ──
  let token: string;
  let currentUserId: string;
  try {
    const loginRes = await httpRequest(LOGIN_URL, {
      method: 'POST',
      body: JSON.stringify(LOGIN_CREDENTIALS),
    });
    if (loginRes.statusCode !== 200 && loginRes.statusCode !== 201) {
      console.error(`❌ 登录失败: HTTP ${loginRes.statusCode}`);
      console.error(loginRes.raw);
      process.exit(1);
    }
    const loginData = loginRes.body?.data || loginRes.body;
    token = loginData?.accessToken || loginData?.access_token;
    currentUserId = loginData?.user?.id || loginData?.userId || '';
    if (!token) {
      console.error('❌ 登录成功但未获取到 accessToken');
      console.error('响应:', JSON.stringify(loginRes.body, null, 2));
      process.exit(1);
    }
    console.log(`✅ 登录成功 (userId: ${currentUserId})\n`);
  } catch (err: any) {
    console.error(`❌ 无法连接后端: ${err.message}`);
    console.error('请确认后端已启动: http://localhost:3001');
    process.exit(1);
  }

  const authHeaders = { Authorization: `Bearer ${token}` };

  // ── 2. 获取已知参数值 ──
  const knownParams: Record<string, string> = {
    currentUserId,
    employeeId: currentUserId,
  };

  // 从模块配置加载已知参数端点，自动获取路径变量值
  const knownParamEndpoints = snapshotConfig?.knownParamEndpoints || {};
  for (const [paramName, config] of Object.entries(knownParamEndpoints) as [string, any][]) {
    try {
      const res = await httpRequest(`${MODULE_BASE}${config.listUrl}`, { headers: authHeaders });
      const data = res.body?.data || res.body;
      const items = data?.items || (Array.isArray(data) ? data : []);
      if (items.length > 0) {
        const selected = config.preferFilter
          ? items.find(config.preferFilter) || items[0]
          : items[0];
        knownParams[paramName] = selected[config.pickField || 'id'];
        console.log(`📋 ${paramName}: ${selected.name || selected.id} (${knownParams[paramName]})`);
      }
    } catch {
      // 忽略，部分端点会被跳过
    }
  }
  console.log('');

  // ── 3. 逐个端点检查 ──
  const results: CheckResult[] = [];
  let hasFailure = false;
  let requestedCount = 0;
  let autoExtractedTotal = extractedEndpoints.length;

  for (const ep of extractedEndpoints) {
    const endpointLabel = `GET ${ep.urlPath} (${ep.functionName})`;
    const override = ENDPOINT_OVERRIDES[ep.functionName];

    // 跳过标记为 skip 的端点
    if (override?.skip) {
      results.push({ status: 'skipped', endpoint: endpointLabel, reason: '手动标记跳过' });
      continue;
    }

    // 构建 URL
    const { url, skippedReason } = buildUrl(ep, knownParams);
    if (skippedReason) {
      results.push({ status: 'skipped', endpoint: endpointLabel, reason: skippedReason });
      continue;
    }

    requestedCount++;

    try {
      const res = await httpRequest(url, { headers: authHeaders });

      // 非 200 处理
      if (res.statusCode >= 400) {
        const b = res.body;
        const msg =
          (typeof b?.error?.message === 'string' ? b.error.message : null) ||
          (typeof b?.data?.message === 'string' ? b.data.message : null) ||
          (typeof b?.message === 'string' ? b.message : null) ||
          (typeof b?.error === 'string' ? b.error : null) ||
          JSON.stringify(b).slice(0, 100);
        const reason = `HTTP ${res.statusCode} - ${msg}`;
        results.push({ status: 'skipped', endpoint: endpointLabel, reason });
        continue;
      }

      // 提取响应数据
      const { data: responseData, isPaginated, isArray, paginationKeys } = extractResponseData(res.body);

      // 确定要检查的数据
      let dataToCompare: any;
      const frontendFields = override?.frontendFields;
      const nestedFields = override?.nestedFields;

      if (isPaginated) {
        // 分页结构：检查 items 中的第一个元素
        const items = responseData?.items;
        if (!items || !Array.isArray(items) || items.length === 0) {
          if (frontendFields) {
            results.push({ status: 'skipped', endpoint: endpointLabel, reason: '返回空列表（分页）' });
          } else {
            results.push({
              status: 'info',
              endpoint: endpointLabel,
              backendFields: paginationKeys || [],
              message: `分页结构，外层字段: ${(paginationKeys || []).join(', ')}，items 为空`,
            });
          }
          continue;
        }
        dataToCompare = items[0];
      } else if (isArray || (responseData && typeof responseData === 'object' && 'items' in responseData && Array.isArray(responseData.items))) {
        // 数组或 items 包装
        const arr = Array.isArray(responseData) ? responseData : (responseData?.items || []);
        if (arr.length === 0) {
          results.push({
            status: frontendFields ? 'skipped' : 'info',
            endpoint: endpointLabel,
            ...(frontendFields
              ? { reason: '返回空列表' }
              : { backendFields: [], message: '返回空列表' }
            ),
          } as any);
          continue;
        }
        dataToCompare = arr[0];
      } else {
        // 单个对象
        dataToCompare = responseData;
      }

      if (dataToCompare === null || dataToCompare === undefined) {
        results.push({
          status: frontendFields ? 'skipped' : 'info',
          endpoint: endpointLabel,
          ...(frontendFields
            ? { reason: '返回 null（可能无数据）' }
            : { backendFields: [], message: '返回 null' }
          ),
        } as any);
        continue;
      }

      if (typeof dataToCompare !== 'object') {
        results.push({
          status: 'info',
          endpoint: endpointLabel,
          backendFields: [],
          message: `返回非对象类型: ${typeof dataToCompare}`,
        });
        continue;
      }

      const backendKeys = extractTopLevelKeys(dataToCompare);

      if (frontendFields && frontendFields.length > 0) {
        // 有手动定义的前端字段 → 严格对比模式
        const result = compareFields(backendKeys, frontendFields, endpointLabel);

        // 嵌套字段检查
        if (nestedFields && dataToCompare) {
          const nestedResults = checkNestedFields(dataToCompare, nestedFields);
          (result as any).nestedResults = nestedResults;
          if (nestedResults.some((n) => n.status === 'mismatch')) hasFailure = true;
        }

        results.push(result);
        if (result.status === 'mismatch') hasFailure = true;
      } else {
        // 无手动定义 → 信息模式，仅输出后端返回了哪些字段
        results.push({
          status: 'info',
          endpoint: endpointLabel,
          backendFields: backendKeys,
          message: `后端返回 ${backendKeys.length} 个字段: ${backendKeys.join(', ')}`,
        });
      }
    } catch (err: any) {
      results.push({ status: 'error', endpoint: endpointLabel, reason: err.message });
    }
  }

  // ── 4. 输出报告 ──
  console.log('─'.repeat(60));
  console.log('');

  let matchCount = 0;
  let mismatchCount = 0;
  let skipCount = 0;
  let warningCount = 0;
  let errorCount = 0;
  let infoCount = 0;

  /** 打印嵌套字段检查结果 */
  function printNestedResults(nestedResults: NestedCheckResult[] | undefined): boolean {
    if (!nestedResults || nestedResults.length === 0) return false;
    let hasNestedIssue = false;

    for (const nr of nestedResults) {
      const label = `${nr.fieldName}[0]`;

      switch (nr.status) {
        case 'match':
          console.log(`  嵌套 ${label}: ✅ 匹配`);
          break;
        case 'mismatch':
          hasNestedIssue = true;
          console.log(`  嵌套 ${label}:`);
          if (nr.backendKeys) {
            console.log(`    后端返回: ${nr.backendKeys.join(', ')}`);
          }
          if (nr.frontendKeys) {
            console.log(`    前端期望: ${nr.frontendKeys.join(', ')}`);
          }
          if (nr.missing && nr.missing.length > 0) {
            console.log(`    → 缺少: ${nr.missing.join(', ')}`);
          }
          if (nr.extra && nr.extra.length > 0) {
            console.log(`    → 多余: ${nr.extra.join(', ')}`);
          }
          break;
        case 'empty':
          console.log(`  嵌套 ${label}: ⏭️  空数据，跳过`);
          break;
        case 'not_array':
          console.log(`  嵌套 ${label}: ⏭️  非对象/数组，跳过`);
          break;
      }
    }
    return hasNestedIssue;
  }

  let nestedMismatchCount = 0;

  for (const r of results) {
    switch (r.status) {
      case 'match': {
        const nested = (r as any).nestedResults as NestedCheckResult[] | undefined;
        const hasNestedIssue = nested?.some((n) => n.status === 'mismatch');
        if (hasNestedIssue) {
          mismatchCount++;
          nestedMismatchCount++;
          console.log(`[❌ 不匹配] ${r.endpoint}`);
          console.log(`  一级字段: ✅ 匹配`);
          printNestedResults(nested);
        } else {
          matchCount++;
          console.log(`[✅ 匹配] ${r.endpoint}`);
          console.log(`  后端返回 ${r.backendCount} 个字段，前端 interface ${r.frontendCount} 个字段，完全一致`);
          printNestedResults(nested);
        }
        console.log('');
        break;
      }

      case 'mismatch': {
        mismatchCount++;
        console.log(`[❌ 不匹配] ${r.endpoint}`);
        if (r.backendOnly.length > 0) {
          console.log(`  后端返回但前端未定义: ${r.backendOnly.join(', ')}`);
        }
        if (r.frontendOnly.length > 0) {
          console.log(`  前端期望但后端未返回: ${r.frontendOnly.join(', ')}`);
        }
        if (r.details) {
          console.log(`  → ${r.details}`);
        }
        printNestedResults((r as any).nestedResults);
        console.log('');
        break;
      }

      case 'backend_missing': {
        warningCount++;
        console.log(`[⚠️  后端缺少] ${r.endpoint}`);
        console.log(`  前端期望但后端未返回: ${r.frontendOnly.join(', ')}`);
        printNestedResults((r as any).nestedResults);
        console.log('');
        break;
      }

      case 'info': {
        infoCount++;
        console.log(`[📋 信息] ${r.endpoint}`);
        console.log(`  ${r.message}`);
        console.log('');
        break;
      }

      case 'skipped':
        skipCount++;
        console.log(`[ℹ️  跳过] ${r.endpoint}`);
        console.log(`  原因: ${r.reason}`);
        console.log('');
        break;

      case 'error':
        errorCount++;
        console.log(`[💥 错误] ${r.endpoint}`);
        console.log(`  ${r.reason}`);
        console.log('');
        break;
    }
  }

  // ── 5. 汇总 ──
  console.log('─'.repeat(60));
  console.log('');
  console.log('=== 汇总 ===');
  console.log(`  ✅ 匹配:   ${matchCount}`);
  console.log(`  ❌ 不匹配: ${mismatchCount}${nestedMismatchCount > 0 ? ` (其中 ${nestedMismatchCount} 个仅嵌套字段不匹配)` : ''}`);
  console.log(`  ⚠️  缺字段: ${warningCount}`);
  console.log(`  📋 信息:   ${infoCount} (无手动配置，仅展示后端字段)`);
  console.log(`  ℹ️  跳过:   ${skipCount}`);
  console.log(`  💥 错误:   ${errorCount}`);
  console.log('');

  // 覆盖率
  const coveredByOverride = Object.keys(ENDPOINT_OVERRIDES).filter(
    (fn) => ENDPOINT_OVERRIDES[fn].frontendFields && ENDPOINT_OVERRIDES[fn].frontendFields!.length > 0,
  ).length;
  console.log('=== 覆盖率 ===');
  console.log(`  前端 GET 函数总数: ${autoExtractedTotal}`);
  console.log(`  实际请求端点数:   ${requestedCount}`);
  console.log(`  严格对比端点数:   ${coveredByOverride} (有 frontendFields 配置)`);
  console.log(`  信息模式端点数:   ${infoCount} (自动发现，无手动配置)`);
  console.log(`  端点覆盖率:       ${((requestedCount / autoExtractedTotal) * 100).toFixed(1)}%`);
  console.log('');

  if (hasFailure) {
    console.log('结果: ❌ 存在字段不匹配，请修复后端或前端 interface');
    process.exit(1);
  } else if (warningCount > 0) {
    console.log('结果: ⚠️  部分字段后端未返回（可能是可选字段），请人工确认');
    process.exit(0);
  } else {
    console.log('结果: ✅ 所有端点检查通过');
    process.exit(0);
  }
}

main().catch((err) => {
  console.error('脚本异常退出:', err.message);
  process.exit(1);
});
