/**
 * Authentication API Integration Tests
 * 
 * 基于 HTTP API 的真正集成测试
 * 
 * 测试内容：
 * - 用户登录/登出
 * - Token 刷新
 * - 密码修改
 * - 身份源验证（LOCAL, LDAP, ENTRA）
 * - 登录安全策略
 * 
 * 基于文档: docs/modules/organization/07-api.md (Section 1: 认证接口)
 * 
 * @version v2.1.1
 */

import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { cleanupDatabase } from '../../helpers/cleanup.helper';
import { createTestApp } from '../../helpers/app.helper';
import { createTestUser } from '../../helpers/factories/user.factory';

describe('Authentication API Integration Tests', () => {
  let app: INestApplication;
  let prisma: PrismaService;

  beforeAll(async () => {
    app = await createTestApp();
    prisma = app.get<PrismaService>(PrismaService);
  });

  beforeEach(async () => {
    // 🎯 清理数据库（认证测试需要干净的环境）
    await cleanupDatabase(prisma);
    // 确保 testuser 被删掉（防止跨 suite 残留）
    await prisma.$executeRawUnsafe(`DELETE FROM platform_iam.users WHERE username = 'testuser'`).catch(() => {});
  });

  afterAll(async () => {
    // 最终清理和关闭应用
    await cleanupDatabase(prisma);
    await app.close();
  });

  /**
   * 🔧 辅助函数：创建用户并登录
   * 简化重复的用户创建和登录逻辑
   */
  async function createUserAndLogin(userData: {
    username: string;
    email: string;
    password: string;
    displayName: string;
    status?: string;
    source?: string;
  }) {
    const user = await createTestUser({
      status: 'ACTIVE',
      source: 'LOCAL',
      ...userData,
    });

    const loginResponse = await request(app.getHttpServer())
      .post('/api/v1/auth/login')
      .send({
        username: userData.username,
        password: userData.password,
      })
      .expect(200);

    return {
      user,
      accessToken: loginResponse.body.data.accessToken,
      refreshToken: loginResponse.body.data.refreshToken,
    };
  }

  describe('POST /api/v1/auth/login - 用户登录', () => {
    beforeEach(async () => {
      // 创建测试用户（使用工厂函数，确保密码正确加密）
      await createTestUser({
        username: 'testuser',
        email: 'test@example.com',
        password: 'Test@123',
        displayName: 'Test User',
        status: 'ACTIVE',
        source: 'LOCAL',
      });
    });

    it('[API-AUTH-001] 应该成功登录并返回Token', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'testuser',
          password: 'Test@123',
        })
        .expect(200);

      // 验证响应格式（符合文档 docs/modules/organization/07-api.md line 548-569）
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('accessToken');
      expect(response.body.data).toHaveProperty('tokenType', 'Bearer');
      expect(response.body.data).toHaveProperty('expiresIn');

      // 验证用户信息
      expect(response.body.data).toHaveProperty('user');
      expect(response.body.data.user.username).toBe('testuser');
      expect(response.body.data.user.email).toBe('test@example.com');
      expect(response.body.data.user).not.toHaveProperty('passwordHash');
    });

    it('[API-AUTH-001b] access token 默认 TTL = 30d 且 expiresIn 与 JWT exp-iat 一致 (09-iam-security.md §2.2)', async () => {
      // 测试环境若未显式设置 JWT_ACCESS_TTL，应走 configuration.ts 兜底默认值 30d。
      // 历史问题：默认值曾是 30m，导致打卡这类一天 2 次的入口必走 refresh，扩大失败面（工单 #242）。
      const THIRTY_DAYS_SEC = 30 * 24 * 60 * 60;
      const envTtl = process.env.JWT_ACCESS_TTL;

      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({ username: 'testuser', password: 'Test@123' })
        .expect(200);

      const expiresIn: number = response.body.data.expiresIn;
      // 没有 env 时必须等于 30d；有 env 时只验证跟 JWT 自身一致（避免锁死非默认值）
      if (!envTtl) {
        expect(expiresIn).toBe(THIRTY_DAYS_SEC);
      }

      const [, payloadB64] = (response.body.data.accessToken as string).split('.');
      const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());
      expect(payload).toHaveProperty('sub');
      expect(payload).toHaveProperty('jti');
      expect(payload).toHaveProperty('exp');
      expect(payload).toHaveProperty('iat');
      // JWT 实际寿命 = exp - iat，必须与登录响应里的 expiresIn 一致（防止两条独立计算路径漂移）
      expect(payload.exp - payload.iat).toBe(expiresIn);
    });

    it('[API-AUTH-002] 错误的用户名应返回401', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'wronguser',
          password: 'Test@123',
        })
        .expect(401);

      expect(response.body.success).toBe(false);
      expect(response.body.error).toBeDefined();
    });

    it('[API-AUTH-003] 错误的密码应返回401', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'testuser',
          password: 'wrongpassword',
        })
        .expect(401);

      expect(response.body.success).toBe(false);
    });

    it('[API-AUTH-004] 停用用户不能登录', async () => {
      // 创建停用用户（使用工厂函数）
      await createTestUser({
        username: 'inactiveuser',
        email: 'inactive@example.com',
        password: 'Test@123',
        displayName: 'Inactive User',
        status: 'INACTIVE',
        source: 'LOCAL',
      });

      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'inactiveuser',
          password: 'Test@123',
        })
        .expect(401);

      expect(response.body.success).toBe(false);
    });

    it('[API-AUTH-005] 缺少必填字段应返回400', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'testuser',
          // 缺少 password
        })
        .expect(400);

      expect(response.body.success).toBe(false);
    });
  });

  describe('POST /api/v1/auth/logout - 用户登出', () => {
    let accessToken: string;

    beforeEach(async () => {
      // 🎯 使用辅助函数创建用户并登录
      const context = await createUserAndLogin({
        username: 'testuser',
        email: 'test@example.com',
        password: 'Test@123',
        displayName: 'Test User',
      });
      accessToken = context.accessToken;
    });

    it('[API-AUTH-006] 应该成功登出', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/logout')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('message');
    });

    it('[API-AUTH-007] 未授权访问应返回401', async () => {
      await request(app.getHttpServer())
        .post('/api/v1/auth/logout')
        .expect(401);
    });

    it('[API-AUTH-008] 无效Token应返回401', async () => {
      await request(app.getHttpServer())
        .post('/api/v1/auth/logout')
        .set('Authorization', 'Bearer invalid_token')
        .expect(401);
    });
  });

  describe('POST /api/v1/auth/refresh - 刷新Token', () => {
    let accessToken: string;
    let refreshToken: string;
    let testUsername: string;

    beforeEach(async () => {
      // 使用随机用户名避免冲突
      testUsername = `testuser_${Date.now()}_${Math.random().toString(36).substring(7)}`;
      
      // 🎯 使用辅助函数创建用户并登录
      const context = await createUserAndLogin({
        username: testUsername,
        email: `${testUsername}@example.com`,
        password: 'Test@123',
        displayName: 'Test User',
      });
      accessToken = context.accessToken;
      refreshToken = context.refreshToken;
    });

    it('[API-AUTH-009] 应该成功刷新Token', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/refresh')
        .send({ refreshToken })
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('accessToken');
      expect(response.body.data).toHaveProperty('refreshToken');
      expect(response.body.data).toHaveProperty('tokenType', 'Bearer');

      // 新 access token 应该不同于旧的
      expect(response.body.data.accessToken).not.toBe(accessToken);
      // refresh token rotation：新 refresh 也应不同
      expect(response.body.data.refreshToken).not.toBe(refreshToken);
    });

    it('[API-AUTH-010] 无效Token不能刷新', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/refresh')
        .send({
          refreshToken: 'invalid_token',
        })
        .expect(401);

      expect(response.body.success).toBe(false);
    });
  });

  describe('POST /api/v1/auth/change-password - 修改密码', () => {
    let accessToken: string;
    let userId: string;

    beforeEach(async () => {
      // 🎯 使用辅助函数创建本地用户并登录
      const context = await createUserAndLogin({
        username: 'testuser',
        email: 'test@example.com',
        password: 'Test@123',
        displayName: 'Test User',
        source: 'LOCAL',
      });
      accessToken = context.accessToken;
      userId = context.user.id;
    });

    it('[API-AUTH-011] 应该成功修改密码（LOCAL用户）', async () => {
      const response = await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'Test@123',
          newPassword: 'NewTest@456',
        })
        .expect(200);

      expect(response.body.success).toBe(true);

      // 验证可以用新密码登录
      const newLoginResponse = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'testuser',
          password: 'NewTest@456',
        })
        .expect(200);

      expect(newLoginResponse.body.success).toBe(true);
    });

    it('[API-AUTH-012] LDAP用户不能修改密码', async () => {
      // 创建LDAP用户
      await prisma.user.update({
        where: { id: userId },
        data: {
          source: 'LDAP',
          ldapDn: 'CN=Test User,OU=Users,DC=company,DC=com',
        },
      });

      const response = await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'Test@123',
          newPassword: 'NewTest@456',
        })
        .expect(400);

      expect(response.body.success).toBe(false);
      expect(response.body.error.code).toContain('CANNOT_CHANGE_PASSWORD');
    });

    it('[API-AUTH-013] ENTRA用户不能修改密码', async () => {
      // 创建ENTRA用户
      await prisma.user.update({
        where: { id: userId },
        data: {
          source: 'ENTRA',
          externalId: 'entra-id-12345',
        },
      });

      const response = await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'Test@123',
          newPassword: 'NewTest@456',
        })
        .expect(400);

      expect(response.body.success).toBe(false);
      expect(response.body.error.code).toContain('CANNOT_CHANGE_PASSWORD');
    });

    it('[API-AUTH-014] 旧密码错误应返回401', async () => {
      const response = await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'WrongPassword@123',
          newPassword: 'NewTest@456',
        })
        .expect(401);

      expect(response.body.success).toBe(false);
    });

    it('[API-AUTH-015] 新密码不符合复杂度要求应返回400', async () => {
      const response = await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'Test@123',
          newPassword: '123', // 太简单
        })
        .expect(400);

      expect(response.body.success).toBe(false);
    });
  });

  describe('POST /api/v1/auth/register - 用户注册', () => {
    it('[API-AUTH-016] 应该成功注册新用户', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/register')
        .send({
          username: 'newuser',
          email: 'newuser@example.com',
          password: 'Test@123',
          displayName: 'New User',
        })
        .expect(201);

      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('user');
      expect(response.body.data).toHaveProperty('token');
      expect(response.body.data.user.username).toBe('newuser');
      expect(response.body.data.user.source).toBe('LOCAL');

      // 验证数据库
      const user = await prisma.user.findUnique({
        where: { username: 'newuser' },
      });

      expect(user).toBeDefined();
      expect(user!.email).toBe('newuser@example.com');
    });

    it('[API-AUTH-017] 用户名已存在应返回409', async () => {
      // 先注册一个用户
      await request(app.getHttpServer())
        .post('/api/v1/auth/register')
        .send({
          username: 'newuser',
          email: 'user1@example.com',
          password: 'Test@123',
          displayName: 'User 1',
        })
        .expect(201);

      // 尝试用相同用户名注册
      const response = await request(app.getHttpServer())
        .post('/api/v1/auth/register')
        .send({
          username: 'newuser',
          email: 'user2@example.com',
          password: 'Test@123',
          displayName: 'User 2',
        })
        .expect(409);

      expect(response.body.success).toBe(false);
    });
  });

  describe('完整认证流程测试', () => {
    it('[API-AUTH-FLOW-001] 应该完成完整的登录-访问-登出流程', async () => {
      // 1. 注册用户
      const registerResponse = await request(app.getHttpServer())
        .post('/api/v1/auth/register')
        .send({
          username: 'flowuser',
          email: 'flow@example.com',
          password: 'Test@123',
          displayName: 'Flow User',
        })
        .expect(201);

      const userId = registerResponse.body.data.user.id;

      // 2. 登录
      const loginResponse = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'flowuser',
          password: 'Test@123',
        })
        .expect(200);

      const accessToken = loginResponse.body.data.accessToken;

      // 3. 访问受保护的资源（查询自己的信息）
      // 使用 /users/me 而不是 /users/:id，因为后者需要 user:read 权限
      const profileResponse = await request(app.getHttpServer())
        .get(`/api/v1/users/me`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200);

      expect(profileResponse.body.data.username).toBe('flowuser');

      // 4. 修改密码
      await request(app.getHttpServer())
        .put('/api/v1/auth/change-password')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          oldPassword: 'Test@123',
          newPassword: 'NewTest@456',
        })
        .expect(200);

      // 5. 登出
      await request(app.getHttpServer())
        .post('/api/v1/auth/logout')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200);

      // 6. 用新密码重新登录
      const newLoginResponse = await request(app.getHttpServer())
        .post('/api/v1/auth/login')
        .send({
          username: 'flowuser',
          password: 'NewTest@456',
        })
        .expect(200);

      expect(newLoginResponse.body.success).toBe(true);

      console.log('✅ 完整认证流程测试通过');
    });
  });
});

