/**
 * 用户内存存储服务单元测试
 * 
 * 测试覆盖：
 * - 基本CRUD操作
 * - 唯一性约束验证
 * - 数据验证
 * - 异常处理
 * - 边缘情况
 * - 性能测试
 * - 批量操作
 * - 搜索功能
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2025-01-07
 * 
 * @lastModified 2026-01-08 by moyin
 * @lastChange 修复代码风格和Mock问题 - 将require语句转换为import语句并修复validate mock (修改者: moyin)
 */

import { Test, TestingModule } from '@nestjs/testing';
import { ConflictException, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { UserStatus } from './user_status.enum';

// Mock 所有外部依赖
const mockValidate = jest.fn().mockResolvedValue([]);

jest.mock('class-validator', () => ({
  validate: mockValidate,
  IsString: () => () => {},
  IsEmail: () => () => {},
  IsPhoneNumber: () => () => {},
  IsInt: () => () => {},
  Min: () => () => {},
  Max: () => () => {},
  IsOptional: () => () => {},
  Length: () => () => {},
  IsNotEmpty: () => () => {},
  IsEnum: () => () => {},
}));

jest.mock('class-transformer', () => ({
  plainToClass: jest.fn((_, obj) => obj),
}));

jest.mock('typeorm', () => ({
  Entity: () => () => {},
  Column: () => () => {},
  PrimaryGeneratedColumn: () => () => {},
  CreateDateColumn: () => () => {},
  UpdateDateColumn: () => () => {},
  OneToOne: () => () => {},
  JoinColumn: () => () => {},
  Index: () => () => {},
}));

// 在 mock 之后导入服务
import { UsersMemoryService } from './users_memory.service';

// 简化的 CreateUserDto 接口
interface CreateUserDto {
  username: string;
  email?: string;
  phone?: string;
  password_hash?: string;
  nickname: string;
  github_id?: string;
  avatar_url?: string;
  role?: number;
  email_verified?: boolean;
  status?: UserStatus;
}

describe('UsersMemoryService', () => {
  let service: UsersMemoryService;
  let loggerSpy: jest.SpyInstance;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UsersMemoryService],
    }).compile();

    service = module.get<UsersMemoryService>(UsersMemoryService);
    
    // Mock Logger methods
    loggerSpy = jest.spyOn(Logger.prototype, 'log').mockImplementation();
    jest.spyOn(Logger.prototype, 'warn').mockImplementation();
    jest.spyOn(Logger.prototype, 'error').mockImplementation();
    
    // Reset validation mock
    mockValidate.mockResolvedValue([]);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('create', () => {
    const validUserDto: CreateUserDto = {
      username: 'testuser',
      email: 'test@example.com',
      nickname: '测试用户',
      password_hash: 'hashedpassword',
      phone: '13800138000',
      github_id: 'github123',
      avatar_url: 'https://example.com/avatar.jpg',
      role: 1,
      email_verified: false,
      status: UserStatus.ACTIVE,
    };

    it('应该成功创建用户', async () => {
      const result = await service.create(validUserDto);

      expect(result).toBeDefined();
      expect(result.id).toBeDefined();
      expect(result.username).toBe(validUserDto.username);
      expect(result.email).toBe(validUserDto.email);
      expect(result.nickname).toBe(validUserDto.nickname);
      expect(result.created_at).toBeInstanceOf(Date);
      expect(result.updated_at).toBeInstanceOf(Date);
      expect(loggerSpy).toHaveBeenCalledWith('开始创建用户', expect.objectContaining({
        context: expect.objectContaining({ username: 'testuser' })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('创建用户成功', expect.objectContaining({
        context: expect.objectContaining({ username: 'testuser' })
      }));
    });

    it('应该为用户分配递增的ID', async () => {
      const user1 = await service.create({ 
        ...validUserDto, 
        username: 'user1', 
        email: 'user1@example.com',
        phone: '13800138001',
        github_id: 'github1' // 不同的GitHub ID
      });
      const user2 = await service.create({ 
        ...validUserDto, 
        username: 'user2', 
        email: 'user2@example.com',
        phone: '13800138002',
        github_id: 'github2' // 不同的GitHub ID
      });

      expect(user2.id).toBe(user1.id + BigInt(1));
    });

    it('应该设置默认值', async () => {
      const minimalDto: CreateUserDto = {
        username: 'minimal',
        nickname: '最小用户',
      };

      const result = await service.create(minimalDto);

      expect(result.email).toBeNull();
      expect(result.phone).toBeNull();
      expect(result.password_hash).toBeNull();
      expect(result.github_id).toBeNull();
      expect(result.avatar_url).toBeNull();
      expect(result.role).toBe(1);
      expect(result.email_verified).toBe(false);
      expect(result.status).toBe(UserStatus.ACTIVE);
    });

    it('应该在数据验证失败时抛出BadRequestException', async () => {
      const validationError = {
        constraints: { isString: 'username must be a string' },
      };
      mockValidate.mockResolvedValueOnce([validationError as any]);

      const testDto = { ...validUserDto, username: 'validation-test' };
      await expect(service.create(testDto)).rejects.toThrow(BadRequestException);
      
      // 新的异常处理不再记录 warn 日志，而是在 handleServiceError 中记录 error 日志
      // 这里我们只验证异常被正确抛出
    });

    it('应该在用户名已存在时抛出ConflictException', async () => {
      await service.create(validUserDto);

      await expect(service.create(validUserDto)).rejects.toThrow(ConflictException);
      await expect(service.create(validUserDto)).rejects.toThrow('用户名已存在');
    });

    it('应该在邮箱已存在时抛出ConflictException', async () => {
      await service.create(validUserDto);
      const duplicateEmailDto = { ...validUserDto, username: 'different' };

      await expect(service.create(duplicateEmailDto)).rejects.toThrow(ConflictException);
      await expect(service.create(duplicateEmailDto)).rejects.toThrow('邮箱已存在');
    });

    it('应该在手机号已存在时抛出ConflictException', async () => {
      await service.create(validUserDto);
      const duplicatePhoneDto = { 
        ...validUserDto, 
        username: 'different', 
        email: 'different@example.com' 
      };

      await expect(service.create(duplicatePhoneDto)).rejects.toThrow(ConflictException);
      await expect(service.create(duplicatePhoneDto)).rejects.toThrow('手机号已存在');
    });

    it('应该在GitHub ID已存在时抛出ConflictException', async () => {
      await service.create(validUserDto);
      const duplicateGithubDto = { 
        ...validUserDto, 
        username: 'different', 
        email: 'different@example.com',
        phone: '13900139000'
      };

      await expect(service.create(duplicateGithubDto)).rejects.toThrow(ConflictException);
      await expect(service.create(duplicateGithubDto)).rejects.toThrow('GitHub ID已存在');
    });

    it('应该记录性能指标', async () => {
      await service.create(validUserDto);

      expect(loggerSpy).toHaveBeenCalledWith('创建用户成功', expect.objectContaining({
        duration: expect.any(Number)
      }));
    });
  });

  describe('findAll', () => {
    beforeEach(async () => {
      // 创建测试数据，确保每个用户都有唯一的标识符
      for (let i = 1; i <= 5; i++) {
        await service.create({
          username: `user${i}`,
          email: `user${i}@example.com`,
          nickname: `用户${i}`,
          phone: `1380013800${i}`, // 确保手机号唯一
        });
        // 添加足够的延迟确保创建时间不同
        await new Promise(resolve => setTimeout(resolve, 10));
      }
    });

    it('应该返回所有用户（默认参数）', async () => {
      const result = await service.findAll();

      expect(result).toHaveLength(5);
      expect(result[0].username).toBe('user5'); // 最新的在前
      expect(result[4].username).toBe('user1'); // 最旧的在后
    });

    it('应该支持分页查询', async () => {
      const result = await service.findAll(2, 1);

      expect(result).toHaveLength(2);
      // 跳过第1个（user5），从第2个开始取2个
      expect(result[0].username).toBe('user4');
      expect(result[1].username).toBe('user3'); // 恢复正确的期望值
    });

    it('应该处理超出范围的分页参数', async () => {
      const result = await service.findAll(10, 10);

      expect(result).toHaveLength(0);
    });

    it('应该记录查询日志', async () => {
      await service.findAll(10, 0);

      expect(loggerSpy).toHaveBeenCalledWith('开始查询所有用户', expect.objectContaining({
        context: expect.objectContaining({ limit: 10, offset: 0 })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('查询所有用户成功', expect.objectContaining({
        context: expect.objectContaining({ resultCount: expect.any(Number) })
      }));
    });
  });

  describe('findOne', () => {
    let userId: bigint;

    beforeEach(async () => {
      const user = await service.create({
        username: 'findtest',
        email: 'findtest@example.com',
        nickname: '查找测试用户',
      });
      userId = user.id;
    });

    it('应该根据ID找到用户', async () => {
      const result = await service.findOne(userId);

      expect(result).toBeDefined();
      expect(result.id).toBe(userId);
      expect(result.username).toBe('findtest');
    });

    it('应该在用户不存在时抛出NotFoundException', async () => {
      const nonExistentId = BigInt(99999);

      await expect(service.findOne(nonExistentId)).rejects.toThrow(NotFoundException);
      await expect(service.findOne(nonExistentId)).rejects.toThrow(`ID为 ${nonExistentId} 的用户不存在`);
    });

    it('应该记录查询日志', async () => {
      await service.findOne(userId);

      expect(loggerSpy).toHaveBeenCalledWith('开始查询用户', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('查询用户成功', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
    });
  });

  describe('findByUsername', () => {
    beforeEach(async () => {
      await service.create({
        username: 'uniqueuser',
        email: 'unique@example.com',
        nickname: '唯一用户',
      });
    });

    it('应该根据用户名找到用户', async () => {
      const result = await service.findByUsername('uniqueuser');

      expect(result).toBeDefined();
      expect(result!.username).toBe('uniqueuser');
    });

    it('应该在用户不存在时返回null', async () => {
      const result = await service.findByUsername('nonexistent');

      expect(result).toBeNull();
    });
  });

  describe('findByEmail', () => {
    beforeEach(async () => {
      await service.create({
        username: 'emailuser',
        email: 'email@example.com',
        nickname: '邮箱用户',
      });
    });

    it('应该根据邮箱找到用户', async () => {
      const result = await service.findByEmail('email@example.com');

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

    it('应该在邮箱不存在时返回null', async () => {
      const result = await service.findByEmail('nonexistent@example.com');

      expect(result).toBeNull();
    });
  });

  describe('findByGithubId', () => {
    beforeEach(async () => {
      await service.create({
        username: 'githubuser',
        email: 'github@example.com',
        nickname: 'GitHub用户',
        github_id: 'github123',
      });
    });

    it('应该根据GitHub ID找到用户', async () => {
      const result = await service.findByGithubId('github123');

      expect(result).toBeDefined();
      expect(result!.github_id).toBe('github123');
    });

    it('应该在GitHub ID不存在时返回null', async () => {
      const result = await service.findByGithubId('nonexistent');

      expect(result).toBeNull();
    });
  });

  describe('update', () => {
    let userId: bigint;

    beforeEach(async () => {
      const user = await service.create({
        username: 'updatetest',
        email: 'update@example.com',
        nickname: '更新测试用户',
        phone: '13800138000',
      });
      userId = user.id;
    });

    it('应该成功更新用户信息', async () => {
      const updateData = {
        nickname: '更新后的昵称',
        email: 'updated@example.com',
      };

      // 添加小延迟确保更新时间不同
      await new Promise(resolve => setTimeout(resolve, 1));
      const result = await service.update(userId, updateData);

      expect(result.nickname).toBe(updateData.nickname);
      expect(result.email).toBe(updateData.email);
      expect(result.updated_at.getTime()).toBeGreaterThan(result.created_at.getTime());
    });

    it('应该在用户不存在时抛出NotFoundException', async () => {
      const nonExistentId = BigInt(99999);

      await expect(service.update(nonExistentId, { nickname: '新昵称' }))
        .rejects.toThrow(NotFoundException);
    });

    it('应该在更新用户名冲突时抛出ConflictException', async () => {
      // 创建另一个用户
      await service.create({
        username: 'another',
        email: 'another@example.com',
        nickname: '另一个用户',
      });

      await expect(service.update(userId, { username: 'another' }))
        .rejects.toThrow(ConflictException);
    });

    it('应该在更新邮箱冲突时抛出ConflictException', async () => {
      await service.create({
        username: 'another',
        email: 'another@example.com',
        nickname: '另一个用户',
      });

      await expect(service.update(userId, { email: 'another@example.com' }))
        .rejects.toThrow(ConflictException);
    });

    it('应该允许更新为相同的值', async () => {
      const result = await service.update(userId, { username: 'updatetest' });

      expect(result.username).toBe('updatetest');
    });

    it('应该记录更新日志', async () => {
      await service.update(userId, { nickname: '新昵称' });

      expect(loggerSpy).toHaveBeenCalledWith('开始更新用户', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('更新用户成功', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
    });
  });

  describe('remove', () => {
    let userId: bigint;

    beforeEach(async () => {
      const user = await service.create({
        username: 'removetest',
        email: 'remove@example.com',
        nickname: '删除测试用户',
      });
      userId = user.id;
    });

    it('应该成功删除用户', async () => {
      const result = await service.remove(userId);

      expect(result.affected).toBe(1);
      expect(result.message).toContain(`成功删除ID为 ${userId} 的用户`);

      // 验证用户已被删除
      await expect(service.findOne(userId)).rejects.toThrow(NotFoundException);
    });

    it('应该在用户不存在时抛出NotFoundException', async () => {
      const nonExistentId = BigInt(99999);

      await expect(service.remove(nonExistentId)).rejects.toThrow(NotFoundException);
    });

    it('应该记录删除日志', async () => {
      await service.remove(userId);

      expect(loggerSpy).toHaveBeenCalledWith('开始删除用户', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('删除用户成功', expect.objectContaining({
        context: expect.objectContaining({ userId: userId.toString() })
      }));
    });
  });

  describe('softRemove', () => {
    let userId: bigint;

    beforeEach(async () => {
      const user = await service.create({
        username: 'softremovetest',
        email: 'softremove@example.com',
        nickname: '软删除测试用户',
      });
      userId = user.id;
    });

    it('应该软删除用户（内存模式下设置删除时间）', async () => {
      const result = await service.softRemove(userId);

      expect(result).toBeDefined();
      expect(result.username).toBe('softremovetest');

      // 验证用户仍然存在（软删除功能暂未实现）
      const foundUser = await service.findOne(userId, true);
      expect(foundUser).toBeDefined();
    });
  });

  describe('count', () => {
    beforeEach(async () => {
      await service.create({
        username: 'count1',
        email: 'count1@example.com',
        nickname: '计数用户1',
        role: 1,
      });
      await service.create({
        username: 'count2',
        email: 'count2@example.com',
        nickname: '计数用户2',
        role: 2,
      });
    });

    it('应该返回总用户数', async () => {
      const result = await service.count();

      expect(result).toBe(2);
    });

    it('应该支持条件查询', async () => {
      const result = await service.count({ role: 1 });

      expect(result).toBe(1);
    });

    it('应该在没有匹配条件时返回0', async () => {
      const result = await service.count({ role: 999 });

      expect(result).toBe(0);
    });
  });

  describe('exists', () => {
    let userId: bigint;

    beforeEach(async () => {
      const user = await service.create({
        username: 'existstest',
        email: 'exists@example.com',
        nickname: '存在测试用户',
      });
      userId = user.id;
    });

    it('应该在用户存在时返回true', async () => {
      const result = await service.exists(userId);

      expect(result).toBe(true);
    });

    it('应该在用户不存在时返回false', async () => {
      const result = await service.exists(BigInt(99999));

      expect(result).toBe(false);
    });
  });

  describe('createBatch', () => {
    const batchData: CreateUserDto[] = [
      {
        username: 'batch1',
        email: 'batch1@example.com',
        nickname: '批量用户1',
      },
      {
        username: 'batch2',
        email: 'batch2@example.com',
        nickname: '批量用户2',
      },
    ];

    it('应该成功批量创建用户', async () => {
      const result = await service.createBatch(batchData);

      expect(result).toHaveLength(2);
      expect(result[0].username).toBe('batch1');
      expect(result[1].username).toBe('batch2');
    });

    it('应该在某个用户创建失败时中断操作', async () => {
      // 先创建一个用户，然后尝试批量创建包含重复用户名的数据
      await service.create(batchData[0]);

      await expect(service.createBatch(batchData)).rejects.toThrow(ConflictException);
    });

    it('应该记录批量操作日志', async () => {
      await service.createBatch(batchData);

      expect(loggerSpy).toHaveBeenCalledWith('开始批量创建用户', expect.objectContaining({
        context: expect.objectContaining({ count: 2 })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('批量创建用户成功', expect.objectContaining({
        context: expect.objectContaining({ createdCount: 2 })
      }));
    });
  });

  describe('findByRole', () => {
    beforeEach(async () => {
      await service.create({
        username: 'admin',
        email: 'admin@example.com',
        nickname: '管理员',
        role: 1,
        phone: '13800138001',
      });
      // 添加延迟确保创建时间不同
      await new Promise(resolve => setTimeout(resolve, 2));
      
      await service.create({
        username: 'user',
        email: 'user@example.com',
        nickname: '普通用户',
        role: 2,
        phone: '13800138002',
      });
      // 添加延迟确保创建时间不同
      await new Promise(resolve => setTimeout(resolve, 2));
      
      await service.create({
        username: 'admin2',
        email: 'admin2@example.com',
        nickname: '管理员2',
        role: 1,
        phone: '13800138003',
      });
    });

    it('应该根据角色查找用户', async () => {
      const admins = await service.findByRole(1);
      const users = await service.findByRole(2);

      expect(admins).toHaveLength(2);
      expect(users).toHaveLength(1);
      expect(admins[0].role).toBe(1);
      expect(users[0].role).toBe(2);
    });

    it('应该按创建时间倒序排列', async () => {
      const admins = await service.findByRole(1);

      expect(admins[0].username).toBe('admin2'); // 最新创建的在前
      expect(admins[1].username).toBe('admin');
    });

    it('应该在没有匹配角色时返回空数组', async () => {
      const result = await service.findByRole(999);

      expect(result).toHaveLength(0);
    });
  });

  describe('search', () => {
    beforeEach(async () => {
      await service.create({
        username: 'admin_user',
        email: 'admin@example.com',
        nickname: '系统管理员',
      });
      await service.create({
        username: 'test_user',
        email: 'test@example.com',
        nickname: '测试用户',
      });
      await service.create({
        username: 'normal_user',
        email: 'normal@example.com',
        nickname: '普通用户',
      });
    });

    it('应该根据用户名搜索用户', async () => {
      const result = await service.search('admin');

      expect(result).toHaveLength(1);
      expect(result[0].username).toBe('admin_user');
    });

    it('应该根据昵称搜索用户', async () => {
      const result = await service.search('管理员');

      expect(result).toHaveLength(1);
      expect(result[0].nickname).toBe('系统管理员');
    });

    it('应该支持大小写不敏感搜索', async () => {
      const result = await service.search('ADMIN');

      expect(result).toHaveLength(1);
      expect(result[0].username).toBe('admin_user');
    });

    it('应该支持部分匹配', async () => {
      const result = await service.search('用户');

      expect(result).toHaveLength(2); // 测试用户 和 普通用户
    });

    it('应该限制返回结果数量', async () => {
      const result = await service.search('user', 1);

      expect(result).toHaveLength(1);
    });

    it('应该在没有匹配结果时返回空数组', async () => {
      const result = await service.search('nonexistent');

      expect(result).toHaveLength(0);
    });

    it('应该记录搜索日志', async () => {
      await service.search('admin');

      expect(loggerSpy).toHaveBeenCalledWith('开始搜索用户', expect.objectContaining({
        context: expect.objectContaining({ keyword: 'admin' })
      }));
      expect(loggerSpy).toHaveBeenCalledWith('搜索用户成功', expect.objectContaining({
        context: expect.objectContaining({ keyword: 'admin' })
      }));
    });
  });

  describe('边缘情况测试', () => {
    it('应该处理空字符串搜索', async () => {
      const result = await service.search('');

      expect(result).toEqual([]);
    });

    it('应该处理极大的分页参数', async () => {
      const result = await service.findAll(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);

      expect(result).toEqual([]);
    });

    it('应该处理负数分页参数', async () => {
      await service.create({
        username: 'testuser',
        email: 'test@example.com',
        nickname: '测试用户',
      });

      const result = await service.findAll(-1, -1);

      expect(result).toEqual([]);
    });

    it('应该处理空的批量创建', async () => {
      const result = await service.createBatch([]);

      expect(result).toEqual([]);
    });

    it('应该处理包含null/undefined字段的更新', async () => {
      const user = await service.create({
        username: 'nulltest',
        email: 'null@example.com',
        nickname: '空值测试',
      });

      const result = await service.update(user.id, {
        email: null as any,
        phone: undefined as any,
      });

      expect(result.email).toBeNull();
    });
  });

  describe('性能测试', () => {
    it('应该在合理时间内完成大量用户创建', async () => {
      const startTime = Date.now();
      const promises = [];

      for (let i = 0; i < 100; i++) {
        promises.push(service.create({
          username: `perfuser${i}`,
          email: `perfuser${i}@example.com`,
          nickname: `性能测试用户${i}`,
        }));
      }

      await Promise.all(promises);
      const duration = Date.now() - startTime;

      expect(duration).toBeLessThan(1000); // 应该在1秒内完成
    });

    it('应该在合理时间内完成大量用户查询', async () => {
      // 先创建一些用户
      for (let i = 0; i < 50; i++) {
        await service.create({
          username: `queryuser${i}`,
          email: `queryuser${i}@example.com`,
          nickname: `查询测试用户${i}`,
        });
      }

      const startTime = Date.now();
      await service.findAll(50, 0);
      const duration = Date.now() - startTime;

      expect(duration).toBeLessThan(100); // 应该在100ms内完成
    });

    it('应该在合理时间内完成搜索操作', async () => {
      // 创建一些用户
      for (let i = 0; i < 50; i++) {
        await service.create({
          username: `searchuser${i}`,
          email: `searchuser${i}@example.com`,
          nickname: `搜索测试用户${i}`,
        });
      }

      const startTime = Date.now();
      await service.search('搜索');
      const duration = Date.now() - startTime;

      expect(duration).toBeLessThan(100); // 应该在100ms内完成
    });
  });

  describe('内存管理测试', () => {
    it('应该正确管理内存中的用户数据', async () => {
      const initialCount = await service.count();

      // 创建用户
      const user = await service.create({
        username: 'memorytest',
        email: 'memory@example.com',
        nickname: '内存测试用户',
      });

      expect(await service.count()).toBe(initialCount + 1);

      // 删除用户
      await service.remove(user.id);

      expect(await service.count()).toBe(initialCount);
    });

    it('应该正确处理ID的递增', async () => {
      const user1 = await service.create({
        username: 'idtest1',
        email: 'idtest1@example.com',
        nickname: 'ID测试用户1',
      });

      const user2 = await service.create({
        username: 'idtest2',
        email: 'idtest2@example.com',
        nickname: 'ID测试用户2',
      });

      expect(user2.id).toBe(user1.id + BigInt(1));

      // 删除用户后，新用户的ID应该继续递增
      await service.remove(user1.id);

      const user3 = await service.create({
        username: 'idtest3',
        email: 'idtest3@example.com',
        nickname: 'ID测试用户3',
      });

      expect(user3.id).toBe(user2.id + BigInt(1));
    });
  });
});