import { RewardSummary } from './reward-summary.aggregate';
import { Money } from '../../value-objects/money.vo';
import { Hashpower } from '../../value-objects/hashpower.vo';

describe('RewardSummary', () => {
  describe('create', () => {
    it('should create a new summary with zero values', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');

      expect(summary.userId).toBe(BigInt(100));
      expect(summary.accountSequence).toBe('D251211000');
      expect(summary.pendingUsdt.amount).toBe(0);
      expect(summary.settleableUsdt.amount).toBe(0);
      expect(summary.settledTotalUsdt.amount).toBe(0);
      expect(summary.expiredTotalUsdt.amount).toBe(0);
    });
  });

  describe('addPending', () => {
    it('should add pending rewards and update expire time', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');
      const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

      summary.addPending(Money.USDT(500), Hashpower.create(5), expireAt);

      expect(summary.pendingUsdt.amount).toBe(500);
      expect(summary.pendingHashpower.value).toBe(5);
      expect(summary.pendingExpireAt).toEqual(expireAt);
    });

    it('should keep earliest expire time', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');
      const earlyExpire = new Date(Date.now() + 12 * 60 * 60 * 1000);
      const lateExpire = new Date(Date.now() + 24 * 60 * 60 * 1000);

      summary.addPending(Money.USDT(500), Hashpower.zero(), earlyExpire);
      summary.addPending(Money.USDT(300), Hashpower.zero(), lateExpire);

      expect(summary.pendingExpireAt).toEqual(earlyExpire);
      expect(summary.pendingUsdt.amount).toBe(800);
    });
  });

  describe('movePendingToSettleable', () => {
    it('should move amounts from pending to settleable', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');
      const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

      summary.addPending(Money.USDT(500), Hashpower.create(5), expireAt);
      summary.movePendingToSettleable(Money.USDT(500), Hashpower.create(5));

      expect(summary.pendingUsdt.amount).toBe(0);
      expect(summary.pendingHashpower.value).toBe(0);
      expect(summary.settleableUsdt.amount).toBe(500);
      expect(summary.settleableHashpower.value).toBe(5);
      expect(summary.pendingExpireAt).toBeNull(); // Cleared when pending is zero
    });

    it('should partially move pending amounts', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');
      const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

      summary.addPending(Money.USDT(500), Hashpower.create(5), expireAt);
      summary.movePendingToSettleable(Money.USDT(300), Hashpower.create(3));

      expect(summary.pendingUsdt.amount).toBe(200);
      expect(summary.pendingHashpower.value).toBe(2);
      expect(summary.settleableUsdt.amount).toBe(300);
      expect(summary.settleableHashpower.value).toBe(3);
    });
  });

  describe('movePendingToExpired', () => {
    it('should move amounts from pending to expired', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');
      const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

      summary.addPending(Money.USDT(500), Hashpower.create(5), expireAt);
      summary.movePendingToExpired(Money.USDT(500), Hashpower.create(5));

      expect(summary.pendingUsdt.amount).toBe(0);
      expect(summary.expiredTotalUsdt.amount).toBe(500);
      expect(summary.expiredTotalHashpower.value).toBe(5);
    });
  });

  describe('addSettleable', () => {
    it('should add directly to settleable', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');

      summary.addSettleable(Money.USDT(1000), Hashpower.create(10));

      expect(summary.settleableUsdt.amount).toBe(1000);
      expect(summary.settleableHashpower.value).toBe(10);
    });
  });

  describe('settle', () => {
    it('should move settleable to settled total', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');

      summary.addSettleable(Money.USDT(1000), Hashpower.create(10));
      summary.settle(Money.USDT(1000), Hashpower.create(10));

      expect(summary.settleableUsdt.amount).toBe(0);
      expect(summary.settledTotalUsdt.amount).toBe(1000);
      expect(summary.settledTotalHashpower.value).toBe(10);
    });

    it('should accumulate settled totals', () => {
      const summary = RewardSummary.create(BigInt(100), 'D251211000');

      summary.addSettleable(Money.USDT(1000), Hashpower.create(10));
      summary.settle(Money.USDT(500), Hashpower.create(5));
      summary.settle(Money.USDT(300), Hashpower.create(3));

      expect(summary.settleableUsdt.amount).toBe(200);
      expect(summary.settledTotalUsdt.amount).toBe(800);
      expect(summary.settledTotalHashpower.value).toBe(8);
    });
  });

  describe('reconstitute', () => {
    it('should rebuild aggregate from persistence data', () => {
      const data = {
        id: BigInt(1),
        userId: BigInt(100),
        accountSequence: 'D251211000',
        pendingUsdt: 500,
        pendingHashpower: 5,
        pendingExpireAt: new Date(),
        settleableUsdt: 1000,
        settleableHashpower: 10,
        settledTotalUsdt: 5000,
        settledTotalHashpower: 50,
        expiredTotalUsdt: 200,
        expiredTotalHashpower: 2,
        lastUpdateAt: new Date(),
        createdAt: new Date(),
      };

      const summary = RewardSummary.reconstitute(data);

      expect(summary.id).toBe(BigInt(1));
      expect(summary.pendingUsdt.amount).toBe(500);
      expect(summary.settleableUsdt.amount).toBe(1000);
      expect(summary.settledTotalUsdt.amount).toBe(5000);
      expect(summary.expiredTotalUsdt.amount).toBe(200);
    });
  });
});
