import { Test, TestingModule } from '@nestjs/testing';
import { StoreBackupShareHandler } from '../../../src/application/commands/store-backup-share/store-backup-share.handler';
import { StoreBackupShareCommand } from '../../../src/application/commands/store-backup-share/store-backup-share.command';
import { BACKUP_SHARE_REPOSITORY } from '../../../src/domain';
import { AesEncryptionService } from '../../../src/infrastructure/crypto/aes-encryption.service';
import { AuditLogRepository } from '../../../src/infrastructure/persistence/repositories/audit-log.repository';
import { ApplicationError } from '../../../src/application/errors/application.error';

describe('StoreBackupShareHandler', () => {
  let handler: StoreBackupShareHandler;
  let mockRepository: any;
  let mockEncryptionService: any;
  let mockAuditLogRepository: any;

  beforeEach(async () => {
    mockRepository = {
      findByUserId: jest.fn(),
      findByPublicKey: jest.fn(),
      save: jest.fn(),
    };

    mockEncryptionService = {
      encrypt: jest.fn().mockResolvedValue({
        encrypted: 'double-encrypted-data',
        keyId: 'key-v1',
      }),
    };

    mockAuditLogRepository = {
      log: jest.fn().mockResolvedValue(undefined),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        StoreBackupShareHandler,
        { provide: BACKUP_SHARE_REPOSITORY, useValue: mockRepository },
        { provide: AesEncryptionService, useValue: mockEncryptionService },
        { provide: AuditLogRepository, useValue: mockAuditLogRepository },
      ],
    }).compile();

    handler = module.get<StoreBackupShareHandler>(StoreBackupShareHandler);
  });

  it('should be defined', () => {
    expect(handler).toBeDefined();
  });

  describe('execute', () => {
    const validCommand = new StoreBackupShareCommand(
      '12345',
      1001,
      '02' + 'a'.repeat(64),
      'encrypted-share-data',
      'identity-service',
      '127.0.0.1',
    );

    it('should store backup share successfully', async () => {
      mockRepository.findByUserId.mockResolvedValue(null);
      mockRepository.findByPublicKey.mockResolvedValue(null);
      mockRepository.save.mockImplementation((share: any) => {
        share.setShareId(BigInt(1));
        return Promise.resolve(share);
      });

      const result = await handler.execute(validCommand);

      expect(result.shareId).toBe('1');
      expect(mockRepository.findByUserId).toHaveBeenCalledWith(BigInt(12345));
      expect(mockRepository.findByPublicKey).toHaveBeenCalledWith(validCommand.publicKey);
      expect(mockEncryptionService.encrypt).toHaveBeenCalledWith(validCommand.encryptedShareData);
      expect(mockRepository.save).toHaveBeenCalled();
      expect(mockAuditLogRepository.log).toHaveBeenCalledWith(
        expect.objectContaining({
          action: 'STORE',
          success: true,
        }),
      );
    });

    it('should throw error if share already exists for user', async () => {
      mockRepository.findByUserId.mockResolvedValue({ userId: BigInt(12345) });

      await expect(handler.execute(validCommand)).rejects.toThrow(ApplicationError);
      await expect(handler.execute(validCommand)).rejects.toThrow(
        'Backup share already exists for this user',
      );
    });

    it('should throw error if share already exists for public key', async () => {
      mockRepository.findByUserId.mockResolvedValue(null);
      mockRepository.findByPublicKey.mockResolvedValue({ publicKey: validCommand.publicKey });

      await expect(handler.execute(validCommand)).rejects.toThrow(ApplicationError);
      await expect(handler.execute(validCommand)).rejects.toThrow(
        'Backup share already exists for this public key',
      );
    });

    it('should use custom threshold and totalParties if provided', async () => {
      mockRepository.findByUserId.mockResolvedValue(null);
      mockRepository.findByPublicKey.mockResolvedValue(null);
      mockRepository.save.mockImplementation((share: any) => {
        share.setShareId(BigInt(1));
        expect(share.threshold).toBe(3);
        expect(share.totalParties).toBe(5);
        return Promise.resolve(share);
      });

      const commandWithCustomParams = new StoreBackupShareCommand(
        '12345',
        1001,
        '02' + 'a'.repeat(64),
        'encrypted-share-data',
        'identity-service',
        '127.0.0.1',
        3, // threshold
        5, // totalParties
      );

      await handler.execute(commandWithCustomParams);

      expect(mockRepository.save).toHaveBeenCalled();
    });
  });
});
