/**
 * 集成测试设置辅助函数
 * 
 * 统一处理测试前的准备工作：清理数据库、创建管理员、获取 Token
 * 
 * @version v2.2.0
 * @created 2025-12-27
 * @updated 2025-12-27 - 添加测试会话ID支持
 */

import { PrismaClient } from '@prisma/client';
import request from 'supertest';
import { INestApplication } from '@nestjs/common';
import { 
  cleanupDatabase, 
  generateTestSessionId, 
  setTestSessionId, 
  getTestSessionId,
  getTestTimestamp  // 新增：导入 getTestTimestamp
} from './cleanup.helper';
import { createAdminUser } from './factories';

export interface AdminContext {
  adminUser: any;
  adminToken: string;
  adminUsername: string;
  adminPassword: string;
  testSessionId: string; // 新增：测试会话ID
  sessionTimestamp: string; // 新增：会话时间戳（用于创建唯一标识）
}

/**
 * 集成测试通用设置（适用于 API 集成测试）
 * 
 * v2.2.0: 支持测试会话ID隔离
 * 
 * 在每个测试前自动执行：
 * 1. 生成测试会话ID（用于数据隔离）
 * 2. 清理数据库（可选：只清理该会话数据）
 * 3. 创建管理员用户（随机用户名，标记会话ID）
 * 4. 登录获取 Token
 * 
 * @param app - NestJS 应用实例
 * @param prisma - PrismaService 实例
 * @param options - 可选配置
 * @returns AdminContext - 包含管理员信息、Token 和会话ID
 * 
 * @example
 * ```typescript
 * describe('Users API', () => {
 *   let app: INestApplication;
 *   let prisma: PrismaService;
 *   let adminToken: string;
 *   let testSessionId: string;
 * 
 *   beforeAll(async () => {
 *     app = await createTestApp();
 *     prisma = app.get<PrismaService>(PrismaService);
 *   });
 * 
 *   beforeEach(async () => {
 *     // 🎯 一行代码完成所有设置（包括会话隔离）
 *     const context = await setupIntegrationTest(app, prisma);
 *     adminToken = context.adminToken;
 *     testSessionId = context.testSessionId;
 *   });
 * 
 *   afterEach(async () => {
 *     // 🧹 只清理该会话的数据
 *     await cleanupDatabase(prisma, testSessionId);
 *   });
 * });
 * ```
 */
export async function setupIntegrationTest(
  app: INestApplication,
  prisma: PrismaClient,
  options: {
    useSessionId?: boolean;  // 是否使用会话ID隔离（默认true）
    cleanupBefore?: boolean; // 是否在测试前清理（默认false，推荐在afterEach清理）
  } = {}
): Promise<AdminContext> {
  const { useSessionId = true, cleanupBefore = true } = options;

  // 1. 生成测试会话ID
  const testSessionId = useSessionId ? generateTestSessionId() : '';
  let sessionTimestamp = ''; // 初始化时间戳
  
  if (useSessionId) {
    setTestSessionId(testSessionId);
    sessionTimestamp = getTestTimestamp(false); // 获取纯时间戳（不带随机后缀）
  }

  // 2. 清理测试数据（保留种子角色和权限）
  if (cleanupBefore) {
    await cleanupDatabase(prisma);
  }

  // 3. 创建临时管理员（每次测试独立，cleanup 会删除）
  const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
  const adminUsername = `t_${uniqueSuffix}_admin`;
  const adminPassword = 'Admin@123';

  const adminUser = await createAdminUser({
    username: adminUsername,
    email: `${adminUsername}@test.com`,
    password: adminPassword,
  });

  // 4. 登录获取 Token
  const loginResponse = await request(app.getHttpServer())
    .post('/api/v1/auth/login')
    .send({
      username: adminUsername,
      password: adminPassword,
    });

  if (loginResponse.status !== 200) {
    throw new Error(
      `管理员登录失败: ${loginResponse.status} - ${JSON.stringify(loginResponse.body)}`
    );
  }

  const adminToken = loginResponse.body.data.accessToken;

  return {
    adminUser,
    adminToken,
    adminUsername,
    adminPassword,
    testSessionId,
    sessionTimestamp,
  };
}

/**
 * 集成测试通用设置（扩展版）
 * 
 * 除了基本的管理员设置外，还可以创建组织、部门等初始数据
 * 
 * @param app - NestJS 应用实例
 * @param prisma - PrismaService 实例
 * @param options - 扩展选项
 * @returns 包含管理员信息、Token 和初始化的资源
 * 
 * @example
 * ```typescript
 * beforeEach(async () => {
 *   const context = await setupIntegrationTestWithOrganization(app, prisma, {
 *     createOrganization: true,
 *     organizationCode: 'TEST_ORG',
 *     organizationName: 'Test Organization',
 *   });
 *   
 *   adminToken = context.adminToken;
 *   organizationId = context.organizationId;
 *   rootDepartmentId = context.rootDepartmentId;
 * });
 * ```
 */
export async function setupIntegrationTestWithOrganization(
  app: INestApplication,
  prisma: PrismaClient,
  options: {
    createOrganization?: boolean;
    organizationCode?: string;
    organizationName?: string;
  } = {},
): Promise<AdminContext & { organizationId?: string; rootDepartmentId?: string }> {
  // 1. 基础设置
  const context = await setupIntegrationTest(app, prisma);

  // 2. 创建测试组织（如果需要）
  if (options.createOrganization) {
    const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
    const orgCode = options.organizationCode || `TEST_ORG_${uniqueId}`;
    const orgName = options.organizationName ? `${options.organizationName}_${uniqueId}` : `Test Organization_${uniqueId}`;

    const orgResponse = await request(app.getHttpServer())
      .post('/api/v1/organizations')
      .set('Authorization', `Bearer ${context.adminToken}`)
      .send({
        code: orgCode,
        name: orgName,
      });

    if (orgResponse.status !== 201) {
      throw new Error(
        `组织创建失败: ${orgResponse.status} - ${JSON.stringify(orgResponse.body)}`
      );
    }

    const organizationId = orgResponse.body.data.id;

    // 3. 获取根部门（组织创建时自动生成）
    const rootDept = await prisma.department.findFirst({
      where: {
        organizationId,
        parentId: null,
      },
    });

    if (!rootDept) {
      throw new Error('根部门未找到');
    }

    return {
      ...context,
      organizationId,
      rootDepartmentId: rootDept.id,
    };
  }

  return context;
}

/**
 * 快速创建测试组织（用于特定测试场景）
 * 
 * @param app - NestJS 应用实例
 * @param adminToken - 管理员 Token
 * @param prisma - PrismaClient 实例
 * @param orgData - 组织数据
 * @returns 组织信息（包含 organizationId 和 rootDepartmentId）
 * 
 * @example
 * ```typescript
 * const { organizationId, rootDepartmentId } = await createTestOrganization(
 *   app,
 *   adminToken,
 *   prisma,
 *   { code: 'FF_CHINA', name: 'FF China' }
 * );
 * ```
 */
export async function createTestOrganization(
  app: INestApplication,
  adminToken: string,
  prisma: PrismaClient,
  orgData: {
    code?: string;
    name: string;
  },
) {
  const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
  const code = orgData.code || `ORG_${uniqueId}`;
  const name = `${orgData.name}_${uniqueId}`;

  const response = await request(app.getHttpServer())
    .post('/api/v1/organizations')
    .set('Authorization', `Bearer ${adminToken}`)
    .send({
      code,
      name,
    })
    .expect(201);

  const organizationId = response.body.data.id;

  // 使用传入的 prisma 实例获取根部门
  const rootDept = await prisma.department.findFirst({
    where: {
      organizationId,
      parentId: null,
    },
  });

  if (!rootDept) {
    throw new Error('根部门未找到');
  }

  return {
    organizationId,
    rootDepartmentId: rootDept.id,
    code,
  };
}

/**
 * 等待异步操作完成（如审计日志写入）
 * 
 * @param ms - 等待毫秒数，默认 100ms
 */
export async function waitForAsync(ms: number = 100): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 生成唯一标识符（用于测试数据）
 * 
 * @param prefix - 前缀，默认 'TEST'
 * @returns 唯一字符串
 * 
 * @example
 * ```typescript
 * const orgCode = generateUniqueId('ORG');  // ORG_1703680000000_abc123
 * const username = generateUniqueId('USER'); // USER_1703680000000_def456
 * ```
 */
export function generateUniqueId(prefix: string = 'TEST'): string {
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(7)}`;
}

