/**
 * Value Objects Unit Tests
 */

import {
  SessionId,
  PartyId,
  ShareId,
  Threshold,
  ShareData,
  PublicKey,
  Signature,
  MessageHash,
} from '../../../src/domain/value-objects';

describe('Value Objects', () => {
  describe('SessionId', () => {
    it('should create valid SessionId', () => {
      const sessionId = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
      expect(sessionId.value).toBe('550e8400-e29b-41d4-a716-446655440000');
    });

    it('should generate unique SessionId', () => {
      const sessionId1 = SessionId.generate();
      const sessionId2 = SessionId.generate();
      expect(sessionId1.value).not.toBe(sessionId2.value);
    });

    it('should throw error for invalid format', () => {
      expect(() => SessionId.create('invalid')).toThrow();
      expect(() => SessionId.create('')).toThrow();
    });

    it('should compare equality correctly', () => {
      const sessionId1 = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
      const sessionId2 = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
      const sessionId3 = SessionId.create('660e8400-e29b-41d4-a716-446655440001');

      expect(sessionId1.equals(sessionId2)).toBe(true);
      expect(sessionId1.equals(sessionId3)).toBe(false);
    });
  });

  describe('PartyId', () => {
    it('should create valid PartyId', () => {
      const partyId = PartyId.create('user123-server');
      expect(partyId.value).toBe('user123-server');
    });

    it('should create from components', () => {
      const partyId = PartyId.fromComponents('user123', 'server');
      expect(partyId.value).toBe('user123-server');
    });

    it('should extract identifier and type', () => {
      const partyId = PartyId.create('user123-server');
      expect(partyId.getIdentifier()).toBe('user123');
      expect(partyId.getType()).toBe('server');
    });

    it('should throw error for invalid format', () => {
      expect(() => PartyId.create('')).toThrow();
      expect(() => PartyId.create('nohyphen')).toThrow();
    });
  });

  describe('ShareId', () => {
    it('should create valid ShareId', () => {
      const shareId = ShareId.create('share_1699887766123_abc123xyz');
      expect(shareId.value).toBe('share_1699887766123_abc123xyz');
    });

    it('should generate unique ShareId', () => {
      const shareId1 = ShareId.generate();
      const shareId2 = ShareId.generate();
      expect(shareId1.value).not.toBe(shareId2.value);
      expect(shareId1.value).toMatch(/^share_\d+_[a-z0-9]+$/);
    });

    it('should throw error for invalid format', () => {
      expect(() => ShareId.create('invalid')).toThrow();
    });
  });

  describe('Threshold', () => {
    it('should create valid threshold', () => {
      const threshold = Threshold.create(3, 2);
      expect(threshold.n).toBe(3);
      expect(threshold.t).toBe(2);
      expect(threshold.toString()).toBe('2-of-3');
    });

    it('should provide common configurations', () => {
      const twoOfThree = Threshold.twoOfThree();
      expect(twoOfThree.toString()).toBe('2-of-3');

      const threeOfFive = Threshold.threeOfFive();
      expect(threeOfFive.toString()).toBe('3-of-5');
    });

    it('should validate participants', () => {
      const threshold = Threshold.create(3, 2);
      expect(threshold.validateParticipants(2)).toBe(true);
      expect(threshold.validateParticipants(3)).toBe(true);
      expect(threshold.validateParticipants(1)).toBe(false);
      expect(threshold.validateParticipants(4)).toBe(false);
    });

    it('should throw error for invalid values', () => {
      expect(() => Threshold.create(0, 0)).toThrow();
      expect(() => Threshold.create(2, 3)).toThrow(); // t > n
      expect(() => Threshold.create(3, 1)).toThrow(); // t < 2
    });
  });

  describe('ShareData', () => {
    it('should create valid ShareData', () => {
      const shareData = ShareData.create(
        Buffer.from('encrypted-data'),
        Buffer.from('123456789012'), // 12 bytes
        Buffer.from('1234567890123456'), // 16 bytes
      );

      expect(shareData.encryptedData).toEqual(Buffer.from('encrypted-data'));
    });

    it('should serialize to JSON and back', () => {
      const original = ShareData.create(
        Buffer.from('encrypted-data'),
        Buffer.from('123456789012'),
        Buffer.from('1234567890123456'),
      );

      const json = original.toJSON();
      const restored = ShareData.fromJSON(json);

      expect(restored.encryptedData).toEqual(original.encryptedData);
      expect(restored.iv).toEqual(original.iv);
      expect(restored.authTag).toEqual(original.authTag);
    });

    it('should throw error for invalid IV length', () => {
      expect(() => ShareData.create(
        Buffer.from('data'),
        Buffer.from('short'), // Less than 12 bytes
        Buffer.from('1234567890123456'),
      )).toThrow();
    });

    it('should throw error for invalid authTag length', () => {
      expect(() => ShareData.create(
        Buffer.from('data'),
        Buffer.from('123456789012'),
        Buffer.from('short'), // Less than 16 bytes
      )).toThrow();
    });
  });

  describe('PublicKey', () => {
    it('should create from hex', () => {
      const hex = '03' + '0'.repeat(64);
      const publicKey = PublicKey.fromHex(hex);
      expect(publicKey.toHex()).toBe(hex);
      expect(publicKey.isCompressed()).toBe(true);
    });

    it('should create from base64', () => {
      const buffer = Buffer.alloc(33);
      buffer[0] = 0x03;
      const publicKey = PublicKey.fromBase64(buffer.toString('base64'));
      expect(publicKey.bytes).toHaveLength(33);
    });

    it('should detect compressed vs uncompressed', () => {
      const compressed = PublicKey.create(Buffer.alloc(33, 0x03));
      const uncompressed = PublicKey.create(Buffer.alloc(65, 0x04));

      expect(compressed.isCompressed()).toBe(true);
      expect(uncompressed.isCompressed()).toBe(false);
    });

    it('should throw error for invalid length', () => {
      expect(() => PublicKey.create(Buffer.alloc(32))).toThrow();
      expect(() => PublicKey.create(Buffer.alloc(64))).toThrow();
    });
  });

  describe('Signature', () => {
    it('should create from components', () => {
      const r = Buffer.alloc(32, 0x01);
      const s = Buffer.alloc(32, 0x02);
      const signature = Signature.create(r, s, 0);

      expect(signature.r).toEqual(r);
      expect(signature.s).toEqual(s);
      expect(signature.v).toBe(0);
    });

    it('should parse from hex', () => {
      const hex = '0'.repeat(128); // 64 bytes = r + s
      const signature = Signature.fromHex(hex);
      expect(signature.r).toHaveLength(32);
      expect(signature.s).toHaveLength(32);
    });

    it('should convert to hex', () => {
      const r = Buffer.alloc(32, 0x01);
      const s = Buffer.alloc(32, 0x02);
      const signature = Signature.create(r, s);

      const hex = signature.toHex();
      expect(hex).toHaveLength(128);
    });

    it('should throw error for invalid component length', () => {
      expect(() => Signature.create(Buffer.alloc(31), Buffer.alloc(32))).toThrow();
      expect(() => Signature.create(Buffer.alloc(32), Buffer.alloc(31))).toThrow();
    });
  });

  describe('MessageHash', () => {
    it('should create from hex', () => {
      const hex = '0x' + '0'.repeat(64);
      const hash = MessageHash.fromHex(hex);
      expect(hash.bytes).toHaveLength(32);
    });

    it('should create from hex without prefix', () => {
      const hex = '0'.repeat(64);
      const hash = MessageHash.fromHex(hex);
      expect(hash.bytes).toHaveLength(32);
    });

    it('should convert to hex with prefix', () => {
      const hex = '0'.repeat(64);
      const hash = MessageHash.fromHex(hex);
      expect(hash.toHex()).toBe('0x' + hex);
    });

    it('should throw error for invalid length', () => {
      expect(() => MessageHash.fromHex('0x1234')).toThrow();
    });
  });
});
