/**
 * @ai-summary Admin service unit tests
 * @ai-context Tests business logic for admin management
 */

import { AdminService } from '../../domain/admin.service';
import { AdminRepository } from '../../data/admin.repository';

describe('AdminService', () => {
  let adminService: AdminService;
  let mockRepository: jest.Mocked<AdminRepository>;

  beforeEach(() => {
    mockRepository = {
      getAdminByAuth0Sub: jest.fn(),
      getAdminByEmail: jest.fn(),
      getAllAdmins: jest.fn(),
      getActiveAdmins: jest.fn(),
      createAdmin: jest.fn(),
      revokeAdmin: jest.fn(),
      reinstateAdmin: jest.fn(),
      logAuditAction: jest.fn(),
      getAuditLogs: jest.fn(),
    } as any;

    adminService = new AdminService(mockRepository);
  });

  describe('getAdminByAuth0Sub', () => {
    it('should return admin when found', async () => {
      const mockAdmin = {
        auth0Sub: 'auth0|123456',
        email: 'admin@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'system',
        revokedAt: null,
        updatedAt: new Date(),
      };

      mockRepository.getAdminByAuth0Sub.mockResolvedValue(mockAdmin);

      const result = await adminService.getAdminByAuth0Sub('auth0|123456');

      expect(result).toEqual(mockAdmin);
      expect(mockRepository.getAdminByAuth0Sub).toHaveBeenCalledWith('auth0|123456');
    });

    it('should return null when admin not found', async () => {
      mockRepository.getAdminByAuth0Sub.mockResolvedValue(null);

      const result = await adminService.getAdminByAuth0Sub('auth0|unknown');

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

  describe('createAdmin', () => {
    it('should create new admin and log audit', async () => {
      const mockAdmin = {
        auth0Sub: 'auth0|newadmin',
        email: 'newadmin@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'auth0|existing',
        revokedAt: null,
        updatedAt: new Date(),
      };

      mockRepository.getAdminByEmail.mockResolvedValue(null);
      mockRepository.createAdmin.mockResolvedValue(mockAdmin);
      mockRepository.logAuditAction.mockResolvedValue({} as any);

      const result = await adminService.createAdmin(
        'newadmin@motovaultpro.com',
        'admin',
        'auth0|newadmin',
        'auth0|existing'
      );

      expect(result).toEqual(mockAdmin);
      expect(mockRepository.createAdmin).toHaveBeenCalled();
      expect(mockRepository.logAuditAction).toHaveBeenCalledWith(
        'auth0|existing',
        'CREATE',
        mockAdmin.auth0Sub,
        'admin_user',
        mockAdmin.email,
        expect.any(Object)
      );
    });

    it('should reject if admin already exists', async () => {
      const existingAdmin = {
        auth0Sub: 'auth0|existing',
        email: 'admin@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'system',
        revokedAt: null,
        updatedAt: new Date(),
      };

      mockRepository.getAdminByEmail.mockResolvedValue(existingAdmin);

      await expect(
        adminService.createAdmin('admin@motovaultpro.com', 'admin', 'auth0|new', 'auth0|existing')
      ).rejects.toThrow('already exists');
    });
  });

  describe('revokeAdmin', () => {
    it('should revoke admin when multiple active admins exist', async () => {
      const revokedAdmin = {
        auth0Sub: 'auth0|toadmin',
        email: 'toadmin@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'system',
        revokedAt: new Date(),
        updatedAt: new Date(),
      };

      const activeAdmins = [
        {
          auth0Sub: 'auth0|admin1',
          email: 'admin1@motovaultpro.com',
          role: 'admin',
          createdAt: new Date(),
          createdBy: 'system',
          revokedAt: null,
          updatedAt: new Date(),
        },
        {
          auth0Sub: 'auth0|admin2',
          email: 'admin2@motovaultpro.com',
          role: 'admin',
          createdAt: new Date(),
          createdBy: 'system',
          revokedAt: null,
          updatedAt: new Date(),
        },
      ];

      mockRepository.getActiveAdmins.mockResolvedValue(activeAdmins);
      mockRepository.revokeAdmin.mockResolvedValue(revokedAdmin);
      mockRepository.logAuditAction.mockResolvedValue({} as any);

      const result = await adminService.revokeAdmin('auth0|toadmin', 'auth0|admin1');

      expect(result).toEqual(revokedAdmin);
      expect(mockRepository.revokeAdmin).toHaveBeenCalledWith('auth0|toadmin');
      expect(mockRepository.logAuditAction).toHaveBeenCalled();
    });

    it('should prevent revoking last active admin', async () => {
      const lastAdmin = {
        auth0Sub: 'auth0|lastadmin',
        email: 'last@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'system',
        revokedAt: null,
        updatedAt: new Date(),
      };

      mockRepository.getActiveAdmins.mockResolvedValue([lastAdmin]);

      await expect(
        adminService.revokeAdmin('auth0|lastadmin', 'auth0|lastadmin')
      ).rejects.toThrow('Cannot revoke the last active admin');
    });
  });

  describe('reinstateAdmin', () => {
    it('should reinstate revoked admin and log audit', async () => {
      const reinstatedAdmin = {
        auth0Sub: 'auth0|reinstate',
        email: 'reinstate@motovaultpro.com',
        role: 'admin',
        createdAt: new Date(),
        createdBy: 'system',
        revokedAt: null,
        updatedAt: new Date(),
      };

      mockRepository.reinstateAdmin.mockResolvedValue(reinstatedAdmin);
      mockRepository.logAuditAction.mockResolvedValue({} as any);

      const result = await adminService.reinstateAdmin('auth0|reinstate', 'auth0|admin');

      expect(result).toEqual(reinstatedAdmin);
      expect(mockRepository.reinstateAdmin).toHaveBeenCalledWith('auth0|reinstate');
      expect(mockRepository.logAuditAction).toHaveBeenCalledWith(
        'auth0|admin',
        'REINSTATE',
        'auth0|reinstate',
        'admin_user',
        reinstatedAdmin.email
      );
    });
  });
});
