import { RewardLedgerEntry } from './reward-ledger-entry.aggregate';
import { RewardSource } from '../../value-objects/reward-source.vo';
import { RightType } from '../../value-objects/right-type.enum';
import { RewardStatus } from '../../value-objects/reward-status.enum';
import { Money } from '../../value-objects/money.vo';
import { Hashpower } from '../../value-objects/hashpower.vo';

describe('RewardLedgerEntry', () => {
  const createRewardSource = () =>
    RewardSource.create(RightType.SHARE_RIGHT, 'ORDER001', BigInt(2));

  describe('createPending', () => {
    it('should create a pending reward with 24h expiration', () => {
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
        memo: 'Test reward',
      });

      expect(entry.isPending).toBe(true);
      expect(entry.rewardStatus).toBe(RewardStatus.PENDING);
      expect(entry.usdtAmount.amount).toBe(500);
      expect(entry.expireAt).toBeDefined();
      expect(entry.domainEvents.length).toBe(1);
      expect(entry.domainEvents[0].eventType).toBe('RewardCreated');
    });

    it('should set expireAt to 24 hours from now', () => {
      const before = Date.now();
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });
      const after = Date.now();

      const expectedExpireMin = before + 24 * 60 * 60 * 1000;
      const expectedExpireMax = after + 24 * 60 * 60 * 1000;

      expect(entry.expireAt!.getTime()).toBeGreaterThanOrEqual(expectedExpireMin);
      expect(entry.expireAt!.getTime()).toBeLessThanOrEqual(expectedExpireMax);
    });
  });

  describe('createSettleable', () => {
    it('should create a settleable reward without expiration', () => {
      const entry = RewardLedgerEntry.createSettleable({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.create(5),
        memo: 'Direct reward',
      });

      expect(entry.isSettleable).toBe(true);
      expect(entry.rewardStatus).toBe(RewardStatus.SETTLEABLE);
      expect(entry.expireAt).toBeNull();
      expect(entry.hashpowerAmount.value).toBe(5);
    });
  });

  describe('claim', () => {
    it('should transition pending to settleable', () => {
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });
      entry.clearDomainEvents();

      entry.claim();

      expect(entry.isSettleable).toBe(true);
      expect(entry.claimedAt).toBeDefined();
      expect(entry.expireAt).toBeNull();
      expect(entry.domainEvents.length).toBe(1);
      expect(entry.domainEvents[0].eventType).toBe('RewardClaimed');
    });

    it('should throw error when not pending', () => {
      const entry = RewardLedgerEntry.createSettleable({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });

      expect(() => entry.claim()).toThrow('只有待领取状态才能领取');
    });
  });

  describe('expire', () => {
    it('should transition pending to expired', () => {
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });
      entry.clearDomainEvents();

      entry.expire();

      expect(entry.isExpired).toBe(true);
      expect(entry.expiredAt).toBeDefined();
      expect(entry.domainEvents.length).toBe(1);
      expect(entry.domainEvents[0].eventType).toBe('RewardExpired');
    });

    it('should throw error when not pending', () => {
      const entry = RewardLedgerEntry.createSettleable({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });

      expect(() => entry.expire()).toThrow('只有待领取状态才能过期');
    });
  });

  describe('settle', () => {
    it('should transition settleable to settled', () => {
      const entry = RewardLedgerEntry.createSettleable({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });
      entry.clearDomainEvents();

      entry.settle('BNB', 0.25);

      expect(entry.isSettled).toBe(true);
      expect(entry.settledAt).toBeDefined();
      expect(entry.domainEvents.length).toBe(1);
      expect(entry.domainEvents[0].eventType).toBe('RewardSettled');
    });

    it('should throw error when not settleable', () => {
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });

      expect(() => entry.settle('BNB', 0.25)).toThrow('只有可结算状态才能结算');
    });
  });

  describe('getRemainingTimeMs', () => {
    it('should return remaining time for pending rewards', () => {
      const entry = RewardLedgerEntry.createPending({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });

      const remaining = entry.getRemainingTimeMs();
      const expected24h = 24 * 60 * 60 * 1000;

      expect(remaining).toBeGreaterThan(expected24h - 1000); // Allow 1 second tolerance
      expect(remaining).toBeLessThanOrEqual(expected24h);
    });

    it('should return 0 for settleable rewards', () => {
      const entry = RewardLedgerEntry.createSettleable({
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: Money.USDT(500),
        hashpowerAmount: Hashpower.zero(),
      });

      expect(entry.getRemainingTimeMs()).toBe(0);
    });
  });

  describe('reconstitute', () => {
    it('should rebuild aggregate from persistence data', () => {
      const data = {
        id: BigInt(1),
        userId: BigInt(100),
        accountSequence: 'D251211000',
        rewardSource: createRewardSource(),
        usdtAmount: 500,
        hashpowerAmount: 5,
        rewardStatus: RewardStatus.SETTLEABLE,
        createdAt: new Date(),
        expireAt: null,
        claimedAt: new Date(),
        settledAt: null,
        expiredAt: null,
        memo: 'Test',
      };

      const entry = RewardLedgerEntry.reconstitute(data);

      expect(entry.id).toBe(BigInt(1));
      expect(entry.userId).toBe(BigInt(100));
      expect(entry.usdtAmount.amount).toBe(500);
      expect(entry.hashpowerAmount.value).toBe(5);
      expect(entry.isSettleable).toBe(true);
      expect(entry.domainEvents.length).toBe(0); // No events on reconstitute
    });
  });
});
