import { ReferralRelationship } from '../../../src/domain/aggregates/referral-relationship/referral-relationship.aggregate';
import { ReferralRelationshipCreatedEvent } from '../../../src/domain/events';

describe('ReferralRelationship Aggregate', () => {
  describe('create', () => {
    it('should create referral relationship without referrer', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', null, null);

      expect(relationship.userId).toBe(100n);
      expect(relationship.accountSequence).toBe('D2512120001');
      expect(relationship.referrerId).toBeNull();
      expect(relationship.usedReferralCode).toBeNull();
      expect(relationship.referralCode).toMatch(/^RWA/);
      expect(relationship.referralChain).toEqual([]);
    });

    it('should create referral relationship with referrer', () => {
      const parentChain = [200n, 300n];
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', parentChain);

      expect(relationship.userId).toBe(100n);
      expect(relationship.accountSequence).toBe('D2512120001');
      expect(relationship.referrerId).toBe(50n);
      expect(relationship.usedReferralCode).toBe('RWAREF12345');
      expect(relationship.referralChain).toEqual([50n, 200n, 300n]);
    });

    it('should emit ReferralRelationshipCreatedEvent', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345');

      expect(relationship.domainEvents.length).toBe(1);
      expect(relationship.domainEvents[0]).toBeInstanceOf(ReferralRelationshipCreatedEvent);

      const event = relationship.domainEvents[0] as ReferralRelationshipCreatedEvent;
      expect(event.userId).toBe(100n);
      expect(event.referrerId).toBe(50n);
    });
  });

  describe('reconstitute', () => {
    it('should reconstitute from persistence data', () => {
      const props = {
        id: 1n,
        userId: 100n,
        accountSequence: 'D2512120001',
        referrerId: 50n,
        referralCode: 'RWATEST123',
        usedReferralCode: 'RWAREF12345',
        referralChain: [50n, 200n],
        createdAt: new Date('2024-01-01'),
        updatedAt: new Date('2024-01-02'),
      };

      const relationship = ReferralRelationship.reconstitute(props);

      expect(relationship.id).toBe(1n);
      expect(relationship.userId).toBe(100n);
      expect(relationship.accountSequence).toBe('D2512120001');
      expect(relationship.referrerId).toBe(50n);
      expect(relationship.referralCode).toBe('RWATEST123');
      expect(relationship.usedReferralCode).toBe('RWAREF12345');
      expect(relationship.referralChain).toEqual([50n, 200n]);
    });

    it('should reconstitute without used referral code', () => {
      const props = {
        id: 1n,
        userId: 100n,
        accountSequence: 'D2512120001',
        referrerId: null,
        referralCode: 'RWATEST123',
        usedReferralCode: null,
        referralChain: [],
        createdAt: new Date('2024-01-01'),
        updatedAt: new Date('2024-01-02'),
      };

      const relationship = ReferralRelationship.reconstitute(props);

      expect(relationship.referrerId).toBeNull();
      expect(relationship.usedReferralCode).toBeNull();
    });
  });

  describe('getDirectReferrer', () => {
    it('should return direct referrer', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', [200n]);
      expect(relationship.getDirectReferrer()).toBe(50n);
    });

    it('should return null when no referrer', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', null, null);
      expect(relationship.getDirectReferrer()).toBeNull();
    });
  });

  describe('getReferrerAtLevel', () => {
    it('should return referrer at specific level', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', [200n, 300n]);

      expect(relationship.getReferrerAtLevel(0)).toBe(50n);
      expect(relationship.getReferrerAtLevel(1)).toBe(200n);
      expect(relationship.getReferrerAtLevel(2)).toBe(300n);
    });
  });

  describe('getAllAncestorIds', () => {
    it('should return all ancestor IDs', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', [200n, 300n]);
      expect(relationship.getAllAncestorIds()).toEqual([50n, 200n, 300n]);
    });
  });

  describe('getChainDepth', () => {
    it('should return chain depth', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', [200n, 300n]);
      expect(relationship.getChainDepth()).toBe(3);
    });
  });

  describe('clearDomainEvents', () => {
    it('should clear domain events', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345');
      expect(relationship.domainEvents.length).toBe(1);

      relationship.clearDomainEvents();
      expect(relationship.domainEvents.length).toBe(0);
    });
  });

  describe('toPersistence', () => {
    it('should convert to persistence format with referrer', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', 50n, 'RWAREF12345', [200n]);
      const data = relationship.toPersistence();

      expect(data.userId).toBe(100n);
      expect(data.accountSequence).toBe('D2512120001');
      expect(data.referrerId).toBe(50n);
      expect(data.referralCode).toMatch(/^RWA/);
      expect(data.usedReferralCode).toBe('RWAREF12345');
      expect(data.referralChain).toEqual([50n, 200n]);
    });

    it('should convert to persistence format without referrer', () => {
      const relationship = ReferralRelationship.create(100n, 'D2512120001', null, null);
      const data = relationship.toPersistence();

      expect(data.referrerId).toBeNull();
      expect(data.usedReferralCode).toBeNull();
    });
  });
});
