import {
  BackupShare,
  BackupShareStatus,
} from '../../../src/domain/entities/backup-share.entity';
import { DomainError } from '../../../src/domain/errors/domain.error';

describe('BackupShare Entity', () => {
  const validParams = {
    userId: BigInt(12345),
    accountSequence: BigInt(1001),
    publicKey: '02' + 'a'.repeat(64),
    encryptedShareData: 'encrypted-data-base64',
    encryptionKeyId: 'key-v1',
  };

  describe('create', () => {
    it('should create a backup share with valid parameters', () => {
      const share = BackupShare.create(validParams);

      expect(share.userId).toBe(validParams.userId);
      expect(share.accountSequence).toBe(validParams.accountSequence);
      expect(share.publicKey).toBe(validParams.publicKey);
      expect(share.encryptedShareData).toBe(validParams.encryptedShareData);
      expect(share.encryptionKeyId).toBe(validParams.encryptionKeyId);
      expect(share.partyIndex).toBe(2);
      expect(share.threshold).toBe(2);
      expect(share.totalParties).toBe(3);
      expect(share.status).toBe(BackupShareStatus.ACTIVE);
      expect(share.accessCount).toBe(0);
      expect(share.shareId).toBeNull();
    });

    it('should allow custom threshold and totalParties', () => {
      const share = BackupShare.create({
        ...validParams,
        threshold: 3,
        totalParties: 5,
      });

      expect(share.threshold).toBe(3);
      expect(share.totalParties).toBe(5);
    });
  });

  describe('recordAccess', () => {
    it('should increment access count for active share', () => {
      const share = BackupShare.create(validParams);

      share.recordAccess();

      expect(share.accessCount).toBe(1);
      expect(share.lastAccessedAt).not.toBeNull();
    });

    it('should throw error when accessing revoked share', () => {
      const share = BackupShare.create(validParams);
      share.revoke('TEST_REASON');

      expect(() => share.recordAccess()).toThrow(DomainError);
      expect(() => share.recordAccess()).toThrow(
        'Cannot access revoked or rotated share',
      );
    });
  });

  describe('revoke', () => {
    it('should revoke an active share', () => {
      const share = BackupShare.create(validParams);

      share.revoke('SECURITY_BREACH');

      expect(share.status).toBe(BackupShareStatus.REVOKED);
      expect(share.revokedAt).not.toBeNull();
    });

    it('should throw error when revoking already revoked share', () => {
      const share = BackupShare.create(validParams);
      share.revoke('FIRST_REVOKE');

      expect(() => share.revoke('SECOND_REVOKE')).toThrow(DomainError);
      expect(() => share.revoke('SECOND_REVOKE')).toThrow(
        'Share already revoked',
      );
    });
  });

  describe('rotate', () => {
    it('should rotate encryption for active share', () => {
      const share = BackupShare.create(validParams);
      const newEncryptedData = 'new-encrypted-data';
      const newKeyId = 'key-v2';

      share.rotate(newEncryptedData, newKeyId);

      expect(share.encryptedShareData).toBe(newEncryptedData);
      expect(share.encryptionKeyId).toBe(newKeyId);
      expect(share.status).toBe(BackupShareStatus.ACTIVE);
    });

    it('should throw error when rotating revoked share', () => {
      const share = BackupShare.create(validParams);
      share.revoke('TEST');

      expect(() => share.rotate('new-data', 'key-v2')).toThrow(DomainError);
      expect(() => share.rotate('new-data', 'key-v2')).toThrow(
        'Cannot rotate revoked share',
      );
    });
  });

  describe('isActive', () => {
    it('should return true for active share', () => {
      const share = BackupShare.create(validParams);

      expect(share.isActive()).toBe(true);
    });

    it('should return false for revoked share', () => {
      const share = BackupShare.create(validParams);
      share.revoke('TEST');

      expect(share.isActive()).toBe(false);
    });
  });

  describe('setShareId', () => {
    it('should set share ID when not already set', () => {
      const share = BackupShare.create(validParams);

      share.setShareId(BigInt(1));

      expect(share.shareId).toBe(BigInt(1));
    });

    it('should throw error when share ID already set', () => {
      const share = BackupShare.create(validParams);
      share.setShareId(BigInt(1));

      expect(() => share.setShareId(BigInt(2))).toThrow(DomainError);
      expect(() => share.setShareId(BigInt(2))).toThrow('Share ID already set');
    });
  });

  describe('toProps', () => {
    it('should return all properties', () => {
      const share = BackupShare.create(validParams);
      const props = share.toProps();

      expect(props.userId).toBe(validParams.userId);
      expect(props.accountSequence).toBe(validParams.accountSequence);
      expect(props.publicKey).toBe(validParams.publicKey);
      expect(props.status).toBe(BackupShareStatus.ACTIVE);
      expect(props.partyIndex).toBe(2);
    });
  });

  describe('reconstitute', () => {
    it('should reconstitute from props', () => {
      const original = BackupShare.create(validParams);
      original.setShareId(BigInt(123));
      const props = original.toProps();

      const reconstituted = BackupShare.reconstitute(props);

      expect(reconstituted.shareId).toBe(props.shareId);
      expect(reconstituted.userId).toBe(props.userId);
      expect(reconstituted.status).toBe(props.status);
    });
  });
});
