/**
 * AdminService 单元测试
 * 
 * 功能描述：
 * - 测试管理员业务服务的所有方法
 * - 验证业务逻辑的正确性
 * - 测试异常处理和边界情况
 * 
 * 职责分离：
 * - 业务逻辑测试，不涉及HTTP层
 * - Mock核心服务，专注业务服务逻辑
 * - 验证数据处理和格式化的正确性
 * 
 * 最近修改：
 * - 2026-01-08: 注释规范优化 - 添加文件头注释，完善测试文档说明 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-19
 * @lastModified 2026-01-08
 */

import { NotFoundException, BadRequestException } from '@nestjs/common';
import { AdminService } from './admin.service';
import { AdminCoreService } from '../../core/admin_core/admin_core.service';
import { LogManagementService } from '../../core/utils/logger/log_management.service';
import { Users } from '../../core/db/users/users.entity';
import { UserStatus } from '../user_mgmt/user_status.enum';

describe('AdminService', () => {
  let service: AdminService;

  const adminCoreServiceMock: Pick<AdminCoreService, 'login' | 'resetUserPassword'> = {
    login: jest.fn(),
    resetUserPassword: jest.fn(),
  };

  const usersServiceMock = {
    findAll: jest.fn(),
    findOne: jest.fn(),
    update: jest.fn(),
  };

  const logManagementServiceMock: Pick<LogManagementService, 'getRuntimeLogTail' | 'getLogDirAbsolutePath'> = {
    getRuntimeLogTail: jest.fn(),
    getLogDirAbsolutePath: jest.fn(),
  };

  beforeEach(() => {
    jest.resetAllMocks();
    service = new AdminService(
      adminCoreServiceMock as unknown as AdminCoreService,
      usersServiceMock as any,
      logManagementServiceMock as unknown as LogManagementService,
    );
  });

  it('should login admin successfully', async () => {
    (adminCoreServiceMock.login as jest.Mock).mockResolvedValue({
      admin: { id: '1', username: 'admin', nickname: '管理员', role: 9 },
      access_token: 'token',
      expires_at: 123,
    });

    const res = await service.login('admin', 'Admin123456');

    expect(adminCoreServiceMock.login).toHaveBeenCalledWith({ identifier: 'admin', password: 'Admin123456' });
    expect(res.success).toBe(true);
    expect(res.data?.admin?.role).toBe(9);
    expect(res.message).toBe('管理员登录成功');
  });

  it('should handle login failure', async () => {
    (adminCoreServiceMock.login as jest.Mock).mockRejectedValue(new Error('密码错误'));

    const res = await service.login('admin', 'bad');

    expect(res.success).toBe(false);
    expect(res.error_code).toBe('ADMIN_LOGIN_FAILED');
    expect(res.message).toBe('密码错误');
  });

  it('should handle non-Error login failure', async () => {
    (adminCoreServiceMock.login as jest.Mock).mockRejectedValue('boom');

    const res = await service.login('admin', 'bad');

    expect(res.success).toBe(false);
    expect(res.error_code).toBe('ADMIN_LOGIN_FAILED');
    expect(res.message).toBe('管理员登录失败');
  });

  it('should list users with pagination', async () => {
    const user = {
      id: BigInt(1),
      username: 'u1',
      nickname: 'U1',
      email: 'u1@test.com',
      email_verified: true,
      phone: null,
      avatar_url: null,
      role: 1,
      created_at: new Date('2025-01-01T00:00:00Z'),
      updated_at: new Date('2025-01-02T00:00:00Z'),
    } as unknown as Users;

    usersServiceMock.findAll.mockResolvedValue([user]);

    const res = await service.listUsers(100, 0);

    expect(usersServiceMock.findAll).toHaveBeenCalledWith(100, 0);
    expect(res.success).toBe(true);
    expect(res.data?.users).toHaveLength(1);
    expect(res.data?.users[0]).toMatchObject({
      id: '1',
      username: 'u1',
      nickname: 'U1',
      role: 1,
    });
  });

  it('should get user by id', async () => {
    const user = {
      id: BigInt(3),
      username: 'u3',
      nickname: 'U3',
      email: null,
      email_verified: false,
      phone: '123',
      avatar_url: null,
      role: 1,
      created_at: new Date('2025-01-01T00:00:00Z'),
      updated_at: new Date('2025-01-02T00:00:00Z'),
    } as unknown as Users;

    usersServiceMock.findOne.mockResolvedValue(user);

    const res = await service.getUser(BigInt(3));

    expect(usersServiceMock.findOne).toHaveBeenCalledWith(BigInt(3));
    expect(res.success).toBe(true);
    expect(res.data?.user).toMatchObject({ id: '3', username: 'u3', nickname: 'U3' });
  });

  it('should reset user password', async () => {
    usersServiceMock.findOne.mockResolvedValue({ id: BigInt(2) } as unknown as Users);
    (adminCoreServiceMock.resetUserPassword as jest.Mock).mockResolvedValue(undefined);

    const res = await service.resetPassword(BigInt(2), 'NewPass1234');

    expect(usersServiceMock.findOne).toHaveBeenCalledWith(BigInt(2));
    expect(adminCoreServiceMock.resetUserPassword).toHaveBeenCalledWith(BigInt(2), 'NewPass1234');
    expect(res).toEqual({ success: true, message: '密码重置成功' });
  });

  it('should throw NotFoundException when resetting password for missing user', async () => {
    usersServiceMock.findOne.mockRejectedValue(new Error('not found'));

    await expect(service.resetPassword(BigInt(999), 'NewPass1234')).rejects.toBeInstanceOf(NotFoundException);
  });

  it('should get runtime logs', async () => {
    (logManagementServiceMock.getRuntimeLogTail as jest.Mock).mockResolvedValue({
      file: 'dev.log',
      updated_at: '2025-01-01T00:00:00.000Z',
      lines: ['a', 'b'],
    });

    const res = await service.getRuntimeLogs(2);

    expect(logManagementServiceMock.getRuntimeLogTail).toHaveBeenCalledWith({ lines: 2 });
    expect(res.success).toBe(true);
    expect(res.data?.file).toBe('dev.log');
    expect(res.data?.lines).toEqual(['a', 'b']);
  });

  it('should expose log dir absolute path', () => {
    (logManagementServiceMock.getLogDirAbsolutePath as jest.Mock).mockReturnValue('/abs/logs');

    expect(service.getLogDirAbsolutePath()).toBe('/abs/logs');
    expect(logManagementServiceMock.getLogDirAbsolutePath).toHaveBeenCalled();
  });

  // 测试新增的用户状态管理方法
  describe('updateUserStatus', () => {
    const mockUser = {
      id: BigInt(1),
      username: 'testuser',
      status: UserStatus.ACTIVE
    } as unknown as Users;

    it('should update user status successfully', async () => {
      usersServiceMock.findOne.mockResolvedValue(mockUser);
      usersServiceMock.update.mockResolvedValue({ ...mockUser, status: UserStatus.INACTIVE });

      const result = await service.updateUserStatus(BigInt(1), { status: UserStatus.INACTIVE, reason: 'test' });

      expect(result.success).toBe(true);
      expect(result.message).toBe('用户状态修改成功');
    });

    it('should throw NotFoundException when user not found', async () => {
      usersServiceMock.findOne.mockResolvedValue(null);

      await expect(service.updateUserStatus(BigInt(999), { status: UserStatus.INACTIVE, reason: 'test' }))
        .rejects.toThrow(NotFoundException);
    });

    it('should return error when status unchanged', async () => {
      usersServiceMock.findOne.mockResolvedValue({ ...mockUser, status: UserStatus.INACTIVE });

      await expect(service.updateUserStatus(BigInt(1), { status: UserStatus.INACTIVE, reason: 'test' }))
        .rejects.toThrow(BadRequestException);
    });
  });

  describe('batchUpdateUserStatus', () => {
    it('should batch update user status successfully', async () => {
      const mockUsers = [
        { id: BigInt(1), username: 'user1', status: UserStatus.ACTIVE },
        { id: BigInt(2), username: 'user2', status: UserStatus.ACTIVE }
      ] as unknown as Users[];

      usersServiceMock.findOne
        .mockResolvedValueOnce(mockUsers[0])
        .mockResolvedValueOnce(mockUsers[1]);
      
      usersServiceMock.update
        .mockResolvedValueOnce({ ...mockUsers[0], status: UserStatus.INACTIVE })
        .mockResolvedValueOnce({ ...mockUsers[1], status: UserStatus.INACTIVE });

      const result = await service.batchUpdateUserStatus({
        userIds: ['1', '2'],
        status: UserStatus.INACTIVE,
        reason: 'batch test'
      });

      expect(result.success).toBe(true);
      expect(result.data?.result.success_count).toBe(2);
      expect(result.data?.result.failed_count).toBe(0);
    });

    it('should handle mixed success and failure', async () => {
      usersServiceMock.findOne
        .mockResolvedValueOnce({ id: BigInt(1), status: UserStatus.ACTIVE })
        .mockResolvedValueOnce(null); // User not found

      usersServiceMock.update.mockResolvedValueOnce({ id: BigInt(1), status: UserStatus.INACTIVE });

      const result = await service.batchUpdateUserStatus({
        userIds: ['1', '999'],
        status: UserStatus.INACTIVE,
        reason: 'mixed test'
      });

      expect(result.success).toBe(true);
      expect(result.data?.result.success_count).toBe(1);
      expect(result.data?.result.failed_count).toBe(1);
    });
  });

  describe('getUserStatusStats', () => {
    it('should return user status statistics', async () => {
      const mockUsers = [
        { status: UserStatus.ACTIVE },
        { status: UserStatus.ACTIVE },
        { status: UserStatus.INACTIVE },
        { status: null } // Should default to active
      ] as unknown as Users[];

      usersServiceMock.findAll.mockResolvedValue(mockUsers);

      const result = await service.getUserStatusStats();

      expect(result.success).toBe(true);
      expect(result.data?.stats.active).toBe(3); // 2 active + 1 null (defaults to active)
      expect(result.data?.stats.inactive).toBe(1);
      expect(result.data?.stats.total).toBe(4);
    });

    it('should handle error when getting stats', async () => {
      usersServiceMock.findAll.mockRejectedValue(new Error('Database error'));

      const result = await service.getUserStatusStats();

      expect(result.success).toBe(false);
      expect(result.error_code).toBe('USER_STATUS_STATS_FAILED');
    });
  });
});
