import { ShareId } from '../../../src/domain/value-objects/share-id.vo';
import { EncryptedData } from '../../../src/domain/value-objects/encrypted-data.vo';
import { DomainError } from '../../../src/domain/errors/domain.error';

describe('Value Objects', () => {
  describe('ShareId', () => {
    describe('create', () => {
      it('should create ShareId from bigint', () => {
        const shareId = ShareId.create(BigInt(123));
        expect(shareId.value).toBe(BigInt(123));
      });

      it('should create ShareId from string', () => {
        const shareId = ShareId.create('456');
        expect(shareId.value).toBe(BigInt(456));
      });

      it('should create ShareId from number', () => {
        const shareId = ShareId.create(789);
        expect(shareId.value).toBe(BigInt(789));
      });

      it('should throw error for zero', () => {
        expect(() => ShareId.create(0)).toThrow(DomainError);
        expect(() => ShareId.create(0)).toThrow('ShareId must be a positive number');
      });

      it('should throw error for negative number', () => {
        expect(() => ShareId.create(-1)).toThrow(DomainError);
        expect(() => ShareId.create(BigInt(-100))).toThrow(DomainError);
      });
    });

    describe('toString', () => {
      it('should return string representation', () => {
        const shareId = ShareId.create(12345);
        expect(shareId.toString()).toBe('12345');
      });
    });

    describe('equals', () => {
      it('should return true for equal values', () => {
        const id1 = ShareId.create(100);
        const id2 = ShareId.create(100);
        expect(id1.equals(id2)).toBe(true);
      });

      it('should return false for different values', () => {
        const id1 = ShareId.create(100);
        const id2 = ShareId.create(200);
        expect(id1.equals(id2)).toBe(false);
      });
    });
  });

  describe('EncryptedData', () => {
    describe('create', () => {
      it('should create EncryptedData with valid params', () => {
        const data = EncryptedData.create({
          ciphertext: 'encrypted-content',
          iv: 'initialization-vector',
          authTag: 'authentication-tag',
          keyId: 'key-v1',
        });

        expect(data.ciphertext).toBe('encrypted-content');
        expect(data.iv).toBe('initialization-vector');
        expect(data.authTag).toBe('authentication-tag');
        expect(data.keyId).toBe('key-v1');
      });

      it('should throw error for empty ciphertext', () => {
        expect(() =>
          EncryptedData.create({
            ciphertext: '',
            iv: 'iv',
            authTag: 'tag',
            keyId: 'key',
          }),
        ).toThrow(DomainError);
        expect(() =>
          EncryptedData.create({
            ciphertext: '',
            iv: 'iv',
            authTag: 'tag',
            keyId: 'key',
          }),
        ).toThrow('Ciphertext cannot be empty');
      });

      it('should throw error for empty IV', () => {
        expect(() =>
          EncryptedData.create({
            ciphertext: 'cipher',
            iv: '',
            authTag: 'tag',
            keyId: 'key',
          }),
        ).toThrow('IV cannot be empty');
      });

      it('should throw error for empty auth tag', () => {
        expect(() =>
          EncryptedData.create({
            ciphertext: 'cipher',
            iv: 'iv',
            authTag: '',
            keyId: 'key',
          }),
        ).toThrow('Auth tag cannot be empty');
      });

      it('should throw error for empty key ID', () => {
        expect(() =>
          EncryptedData.create({
            ciphertext: 'cipher',
            iv: 'iv',
            authTag: 'tag',
            keyId: '',
          }),
        ).toThrow('Key ID cannot be empty');
      });
    });

    describe('fromSerializedString', () => {
      it('should parse valid serialized string', () => {
        const serialized = 'ciphertext:iv:authTag';
        const data = EncryptedData.fromSerializedString(serialized, 'key-v1');

        expect(data.ciphertext).toBe('ciphertext');
        expect(data.iv).toBe('iv');
        expect(data.authTag).toBe('authTag');
        expect(data.keyId).toBe('key-v1');
      });

      it('should throw error for invalid format (too few parts)', () => {
        expect(() => EncryptedData.fromSerializedString('only:two', 'key')).toThrow(
          'Invalid encrypted data format',
        );
      });

      it('should throw error for invalid format (too many parts)', () => {
        expect(() =>
          EncryptedData.fromSerializedString('one:two:three:four', 'key'),
        ).toThrow('Invalid encrypted data format');
      });
    });

    describe('toSerializedString', () => {
      it('should serialize to correct format', () => {
        const data = EncryptedData.create({
          ciphertext: 'cipher123',
          iv: 'iv456',
          authTag: 'tag789',
          keyId: 'key-v2',
        });

        expect(data.toSerializedString()).toBe('cipher123:iv456:tag789');
      });

      it('should be reversible', () => {
        const original = EncryptedData.create({
          ciphertext: 'test-cipher',
          iv: 'test-iv',
          authTag: 'test-tag',
          keyId: 'key-v1',
        });

        const serialized = original.toSerializedString();
        const restored = EncryptedData.fromSerializedString(serialized, 'key-v1');

        expect(restored.ciphertext).toBe(original.ciphertext);
        expect(restored.iv).toBe(original.iv);
        expect(restored.authTag).toBe(original.authTag);
      });
    });
  });
});
