/**
 * Dual Currency Commission Property Tests
 * Tests for dual-currency commission calculation correctness
 * 
 * **Feature: dual-currency-commission**
 */

const fc = require('fast-check');

// Mock database first
const mockTransaction = {
  commit: jest.fn(),
  rollback: jest.fn(),
};

jest.mock('../config/database', () => ({
  sequelize: {
    define: jest.fn(() => ({})),
    transaction: jest.fn(() => Promise.resolve(mockTransaction)),
    fn: jest.fn((fnName, ...args) => ({ fn: fnName, args })),
    col: jest.fn((colName) => ({ col: colName })),
  },
}));

// Mock models
jest.mock('../models/User', () => ({
  findByPk: jest.fn(),
  count: jest.fn(),
  findAll: jest.fn(),
}));

jest.mock('../models/Commission', () => ({
  create: jest.fn(),
  findAll: jest.fn(),
  findAndCountAll: jest.fn(),
  findOne: jest.fn(),
  count: jest.fn(),
  sum: jest.fn(),
}));

jest.mock('../models/PaymentOrder', () => ({
  findByPk: jest.fn(),
}));

jest.mock('../services/commissionConfigService', () => ({
  getCommissionRate: jest.fn(),
}));

const User = require('../models/User');
const Commission = require('../models/Commission');
const PaymentOrder = require('../models/PaymentOrder');
const { sequelize } = require('../config/database');
const commissionConfigService = require('../services/commissionConfigService');
const commissionService = require('../services/commissionService');

describe('Dual Currency Commission - Property Tests', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockTransaction.commit.mockClear();
    mockTransaction.rollback.mockClear();
    sequelize.transaction.mockResolvedValue(mockTransaction);
  });

  /**
   * **Feature: dual-currency-commission, Property 1: Commission records match currency presence**
   * *For any* payment order with invited user, the number of commission records created SHALL equal
   * the number of non-zero currency amounts (amountRmb > 0 adds one RMB commission, amountPeso > 0 adds one PHP commission)
   * **Validates: Requirements 1.1, 1.2, 1.3**
   */
  describe('Property 1: Commission records match currency presence', () => {
    it('should create commission records matching non-zero currency amounts', async () => {
      await fc.assert(
        fc.asyncProperty(
          // Generate optional RMB amount (0 means no RMB)
          fc.oneof(
            fc.constant(0),
            fc.double({ min: 0.01, max: 100000, noNaN: true })
          ),
          // Generate optional Peso amount (0 means no Peso)
          fc.oneof(
            fc.constant(0),
            fc.double({ min: 0.01, max: 100000, noNaN: true })
          ),
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (amountRmb, amountPeso, commissionRate) => {
            // Skip if both amounts are zero (no commission expected)
            if (amountRmb === 0 && amountPeso === 0) return;

            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: amountRmb > 0 ? amountRmb : null,
              amountPeso: amountPeso > 0 ? amountPeso : null,
              amount: null, // Legacy field
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(commissionRate);

            const createdCommissions = [];
            Commission.create.mockImplementation((data) => {
              const commission = { ...data, id: `commission-uuid-${createdCommissions.length}` };
              createdCommissions.push(commission);
              return Promise.resolve(commission);
            });

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 0,
              balancePeso: 0,
              save: jest.fn(),
            });

            // Act
            const result = await commissionService.calculateCommission(paymentOrderId);

            // Calculate expected number of commissions
            let expectedCount = 0;
            if (amountRmb > 0) expectedCount++;
            if (amountPeso > 0) expectedCount++;

            // Assert: Number of commission records should match non-zero currency count
            expect(result).not.toBeNull();
            expect(Array.isArray(result)).toBe(true);
            expect(result.length).toBe(expectedCount);
            expect(createdCommissions.length).toBe(expectedCount);

            // Verify currency types
            const currencies = createdCommissions.map(c => c.currency);
            if (amountRmb > 0) {
              expect(currencies).toContain('RMB');
            }
            if (amountPeso > 0) {
              expect(currencies).toContain('PHP');
            }
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should create only RMB commission when only RMB amount is present', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // RMB amount
          async (amountRmb) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: amountRmb,
              amountPeso: null,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(0.02);

            const createdCommissions = [];
            Commission.create.mockImplementation((data) => {
              const commission = { ...data, id: `commission-uuid-${createdCommissions.length}` };
              createdCommissions.push(commission);
              return Promise.resolve(commission);
            });

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 0,
              balancePeso: 0,
              save: jest.fn(),
            });

            // Act
            const result = await commissionService.calculateCommission(paymentOrderId);

            // Assert: Only one RMB commission should be created
            expect(result.length).toBe(1);
            expect(createdCommissions.length).toBe(1);
            expect(createdCommissions[0].currency).toBe('RMB');
            expect(createdCommissions[0].paymentAmount).toBeCloseTo(amountRmb, 8);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should create only PHP commission when only Peso amount is present', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // Peso amount
          async (amountPeso) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: null,
              amountPeso: amountPeso,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(0.02);

            const createdCommissions = [];
            Commission.create.mockImplementation((data) => {
              const commission = { ...data, id: `commission-uuid-${createdCommissions.length}` };
              createdCommissions.push(commission);
              return Promise.resolve(commission);
            });

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 0,
              balancePeso: 0,
              save: jest.fn(),
            });

            // Act
            const result = await commissionService.calculateCommission(paymentOrderId);

            // Assert: Only one PHP commission should be created
            expect(result.length).toBe(1);
            expect(createdCommissions.length).toBe(1);
            expect(createdCommissions[0].currency).toBe('PHP');
            expect(createdCommissions[0].paymentAmount).toBeCloseTo(amountPeso, 8);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should create two commissions when both RMB and Peso amounts are present', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // RMB amount
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // Peso amount
          async (amountRmb, amountPeso) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: amountRmb,
              amountPeso: amountPeso,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(0.02);

            const createdCommissions = [];
            Commission.create.mockImplementation((data) => {
              const commission = { ...data, id: `commission-uuid-${createdCommissions.length}` };
              createdCommissions.push(commission);
              return Promise.resolve(commission);
            });

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 0,
              balancePeso: 0,
              save: jest.fn(),
            });

            // Act
            const result = await commissionService.calculateCommission(paymentOrderId);

            // Assert: Two commissions should be created (one RMB, one PHP)
            expect(result.length).toBe(2);
            expect(createdCommissions.length).toBe(2);
            
            const currencies = createdCommissions.map(c => c.currency);
            expect(currencies).toContain('RMB');
            expect(currencies).toContain('PHP');

            // Verify amounts match
            const rmbCommission = createdCommissions.find(c => c.currency === 'RMB');
            const phpCommission = createdCommissions.find(c => c.currency === 'PHP');
            expect(rmbCommission.paymentAmount).toBeCloseTo(amountRmb, 8);
            expect(phpCommission.paymentAmount).toBeCloseTo(amountPeso, 8);
          }
        ),
        { numRuns: 100 }
      );
    });
  });
});


  /**
   * **Feature: dual-currency-commission, Property 2: Balance updates match commission currencies**
   * *For any* commission creation, the inviter's balance SHALL increase by the RMB commission amount
   * and balancePeso SHALL increase by the PHP commission amount
   * **Validates: Requirements 1.4**
   */
  describe('Property 2: Balance updates match commission currencies', () => {
    it('should update RMB balance when RMB commission is created', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // RMB amount
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial RMB balance
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (amountRmb, initialBalance, commissionRate) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: amountRmb,
              amountPeso: null,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(commissionRate);

            Commission.create.mockImplementation((data) => {
              return Promise.resolve({ ...data, id: 'commission-uuid' });
            });

            let updatedBalance = null;
            let updatedBalancePeso = null;
            const mockInviter = {
              id: inviterId,
              balance: initialBalance,
              balancePeso: 0,
              save: jest.fn().mockImplementation(function() {
                updatedBalance = this.balance;
                updatedBalancePeso = this.balancePeso;
                return Promise.resolve();
              }),
            };
            User.findByPk.mockResolvedValue(mockInviter);

            // Act
            await commissionService.calculateCommission(paymentOrderId);

            // Assert: RMB balance should increase by commission amount
            const expectedCommission = amountRmb * commissionRate;
            const expectedBalance = initialBalance + expectedCommission;
            expect(updatedBalance).toBeCloseTo(expectedBalance, 8);
            // Peso balance should remain unchanged
            expect(updatedBalancePeso).toBe(0);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should update Peso balance when PHP commission is created', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // Peso amount
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial Peso balance
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (amountPeso, initialBalancePeso, commissionRate) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: null,
              amountPeso: amountPeso,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(commissionRate);

            Commission.create.mockImplementation((data) => {
              return Promise.resolve({ ...data, id: 'commission-uuid' });
            });

            let updatedBalance = null;
            let updatedBalancePeso = null;
            const mockInviter = {
              id: inviterId,
              balance: 0,
              balancePeso: initialBalancePeso,
              save: jest.fn().mockImplementation(function() {
                updatedBalance = this.balance;
                updatedBalancePeso = this.balancePeso;
                return Promise.resolve();
              }),
            };
            User.findByPk.mockResolvedValue(mockInviter);

            // Act
            await commissionService.calculateCommission(paymentOrderId);

            // Assert: Peso balance should increase by commission amount
            const expectedCommission = amountPeso * commissionRate;
            const expectedBalancePeso = initialBalancePeso + expectedCommission;
            expect(updatedBalancePeso).toBeCloseTo(expectedBalancePeso, 8);
            // RMB balance should remain unchanged
            expect(updatedBalance).toBe(0);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should update both balances when dual-currency commission is created', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // RMB amount
          fc.double({ min: 0.01, max: 100000, noNaN: true }), // Peso amount
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial RMB balance
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial Peso balance
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (amountRmb, amountPeso, initialBalance, initialBalancePeso, commissionRate) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';
            const paymentOrderId = 'order-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: paymentOrderId,
              amountRmb: amountRmb,
              amountPeso: amountPeso,
              amount: null,
              status: 'active',
              user: {
                id: inviteeId,
                invitedBy: inviterId,
              },
            });

            commissionConfigService.getCommissionRate.mockResolvedValue(commissionRate);

            Commission.create.mockImplementation((data) => {
              return Promise.resolve({ ...data, id: 'commission-uuid' });
            });

            let updatedBalance = null;
            let updatedBalancePeso = null;
            const mockInviter = {
              id: inviterId,
              balance: initialBalance,
              balancePeso: initialBalancePeso,
              save: jest.fn().mockImplementation(function() {
                updatedBalance = this.balance;
                updatedBalancePeso = this.balancePeso;
                return Promise.resolve();
              }),
            };
            User.findByPk.mockResolvedValue(mockInviter);

            // Act
            await commissionService.calculateCommission(paymentOrderId);

            // Assert: Both balances should increase by their respective commission amounts
            const expectedRmbCommission = amountRmb * commissionRate;
            const expectedPesoCommission = amountPeso * commissionRate;
            const expectedBalance = initialBalance + expectedRmbCommission;
            const expectedBalancePeso = initialBalancePeso + expectedPesoCommission;
            
            expect(updatedBalance).toBeCloseTo(expectedBalance, 8);
            expect(updatedBalancePeso).toBeCloseTo(expectedBalancePeso, 8);
          }
        ),
        { numRuns: 100 }
      );
    });
  });


/**
 * Property 3: Cancellation reverses all commissions
 * Tests for payment order cancellation with dual-currency commission handling
 */
describe('Property 3: Cancellation reverses all commissions', () => {
  /**
   * **Feature: dual-currency-commission, Property 3: Cancellation reverses all commissions**
   * *For any* payment order cancellation, all associated commission records SHALL be cancelled
   * and the inviter's balances SHALL be decremented by the respective commission amounts (with floor at zero)
   * **Validates: Requirements 3.1, 3.2, 3.3**
   */

  // We need to mock paymentOrderService for cancellation tests
  const mockPaymentOrderTransaction = {
    commit: jest.fn(),
    rollback: jest.fn(),
  };

  beforeEach(() => {
    jest.clearAllMocks();
    mockPaymentOrderTransaction.commit.mockClear();
    mockPaymentOrderTransaction.rollback.mockClear();
    sequelize.transaction.mockResolvedValue(mockPaymentOrderTransaction);
  });

  it('should cancel all commission records when order is cancelled', async () => {
    await fc.assert(
      fc.asyncProperty(
        fc.double({ min: 0.01, max: 100000, noNaN: true }), // RMB commission amount
        fc.double({ min: 0.01, max: 100000, noNaN: true }), // Peso commission amount
        async (rmbCommissionAmount, pesoCommissionAmount) => {
          // Arrange
          const orderId = 'order-uuid';
          const inviterId = 'inviter-uuid';
          
          // Mock PaymentOrder.findByPk for cancellation
          const mockOrder = {
            id: orderId,
            status: 'active',
            cancelReason: null,
            save: jest.fn().mockResolvedValue(true),
          };
          PaymentOrder.findByPk.mockResolvedValue(mockOrder);
          
          // Mock commissions (dual-currency)
          const mockCommissions = [
            {
              id: 'commission-rmb-uuid',
              inviterId: inviterId,
              paymentOrderId: orderId,
              commissionAmount: rmbCommissionAmount,
              currency: 'RMB',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
            {
              id: 'commission-php-uuid',
              inviterId: inviterId,
              paymentOrderId: orderId,
              commissionAmount: pesoCommissionAmount,
              currency: 'PHP',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
          ];
          Commission.findAll.mockResolvedValue(mockCommissions);
          
          // Mock inviter
          const mockInviter = {
            id: inviterId,
            balance: rmbCommissionAmount + 100, // Ensure enough balance
            balancePeso: pesoCommissionAmount + 100,
            save: jest.fn().mockResolvedValue(true),
          };
          User.findByPk.mockResolvedValue(mockInviter);
          
          // Act - Import and call cancelPaymentOrder
          const paymentOrderService = require('../services/paymentOrderService');
          const result = await paymentOrderService.cancelPaymentOrder(orderId, 'Test cancellation');
          
          // Assert: All commissions should be cancelled
          expect(result.commissionsCancelledCount).toBe(2);
          expect(mockCommissions[0].status).toBe('cancelled');
          expect(mockCommissions[1].status).toBe('cancelled');
          expect(mockCommissions[0].save).toHaveBeenCalled();
          expect(mockCommissions[1].save).toHaveBeenCalled();
        }
      ),
      { numRuns: 50 }
    );
  });

  it('should deduct correct amounts from respective currency balances', async () => {
    await fc.assert(
      fc.asyncProperty(
        fc.double({ min: 0.01, max: 1000, noNaN: true }), // RMB commission amount
        fc.double({ min: 0.01, max: 1000, noNaN: true }), // Peso commission amount
        fc.double({ min: 1000, max: 10000, noNaN: true }), // Initial RMB balance (high enough)
        fc.double({ min: 1000, max: 10000, noNaN: true }), // Initial Peso balance (high enough)
        async (rmbCommission, pesoCommission, initialRmbBalance, initialPesoBalance) => {
          // Arrange
          const orderId = 'order-uuid';
          const inviterId = 'inviter-uuid';
          
          const mockOrder = {
            id: orderId,
            status: 'active',
            cancelReason: null,
            save: jest.fn().mockResolvedValue(true),
          };
          PaymentOrder.findByPk.mockResolvedValue(mockOrder);
          
          const mockCommissions = [
            {
              id: 'commission-rmb-uuid',
              inviterId: inviterId,
              commissionAmount: rmbCommission,
              currency: 'RMB',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
            {
              id: 'commission-php-uuid',
              inviterId: inviterId,
              commissionAmount: pesoCommission,
              currency: 'PHP',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
          ];
          Commission.findAll.mockResolvedValue(mockCommissions);
          
          let finalRmbBalance = null;
          let finalPesoBalance = null;
          const mockInviter = {
            id: inviterId,
            balance: initialRmbBalance,
            balancePeso: initialPesoBalance,
            save: jest.fn().mockImplementation(function() {
              finalRmbBalance = this.balance;
              finalPesoBalance = this.balancePeso;
              return Promise.resolve();
            }),
          };
          User.findByPk.mockResolvedValue(mockInviter);
          
          // Act
          const paymentOrderService = require('../services/paymentOrderService');
          await paymentOrderService.cancelPaymentOrder(orderId, 'Test cancellation');
          
          // Assert: Balances should be decremented correctly
          const expectedRmbBalance = initialRmbBalance - rmbCommission;
          const expectedPesoBalance = initialPesoBalance - pesoCommission;
          
          expect(finalRmbBalance).toBeCloseTo(expectedRmbBalance, 8);
          expect(finalPesoBalance).toBeCloseTo(expectedPesoBalance, 8);
        }
      ),
      { numRuns: 50 }
    );
  });

  it('should floor balance at zero when deduction exceeds balance', async () => {
    await fc.assert(
      fc.asyncProperty(
        fc.double({ min: 100, max: 1000, noNaN: true }), // RMB commission (larger than balance)
        fc.double({ min: 100, max: 1000, noNaN: true }), // Peso commission (larger than balance)
        fc.double({ min: 0, max: 50, noNaN: true }), // Initial RMB balance (smaller than commission)
        fc.double({ min: 0, max: 50, noNaN: true }), // Initial Peso balance (smaller than commission)
        async (rmbCommission, pesoCommission, initialRmbBalance, initialPesoBalance) => {
          // Arrange
          const orderId = 'order-uuid';
          const inviterId = 'inviter-uuid';
          
          const mockOrder = {
            id: orderId,
            status: 'active',
            cancelReason: null,
            save: jest.fn().mockResolvedValue(true),
          };
          PaymentOrder.findByPk.mockResolvedValue(mockOrder);
          
          const mockCommissions = [
            {
              id: 'commission-rmb-uuid',
              inviterId: inviterId,
              commissionAmount: rmbCommission,
              currency: 'RMB',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
            {
              id: 'commission-php-uuid',
              inviterId: inviterId,
              commissionAmount: pesoCommission,
              currency: 'PHP',
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
          ];
          Commission.findAll.mockResolvedValue(mockCommissions);
          
          let finalRmbBalance = null;
          let finalPesoBalance = null;
          const mockInviter = {
            id: inviterId,
            balance: initialRmbBalance,
            balancePeso: initialPesoBalance,
            save: jest.fn().mockImplementation(function() {
              finalRmbBalance = this.balance;
              finalPesoBalance = this.balancePeso;
              return Promise.resolve();
            }),
          };
          User.findByPk.mockResolvedValue(mockInviter);
          
          // Act
          const paymentOrderService = require('../services/paymentOrderService');
          await paymentOrderService.cancelPaymentOrder(orderId, 'Test cancellation');
          
          // Assert: Balances should be floored at zero
          expect(finalRmbBalance).toBe(0);
          expect(finalPesoBalance).toBe(0);
        }
      ),
      { numRuns: 50 }
    );
  });

  it('should handle single currency commission cancellation correctly', async () => {
    await fc.assert(
      fc.asyncProperty(
        fc.double({ min: 0.01, max: 1000, noNaN: true }), // Commission amount
        fc.constantFrom('RMB', 'PHP'), // Currency type
        fc.double({ min: 1000, max: 10000, noNaN: true }), // Initial balance
        async (commissionAmount, currency, initialBalance) => {
          // Arrange
          const orderId = 'order-uuid';
          const inviterId = 'inviter-uuid';
          
          const mockOrder = {
            id: orderId,
            status: 'active',
            cancelReason: null,
            save: jest.fn().mockResolvedValue(true),
          };
          PaymentOrder.findByPk.mockResolvedValue(mockOrder);
          
          // Single commission
          const mockCommissions = [
            {
              id: 'commission-uuid',
              inviterId: inviterId,
              commissionAmount: commissionAmount,
              currency: currency,
              status: 'credited',
              save: jest.fn().mockResolvedValue(true),
            },
          ];
          Commission.findAll.mockResolvedValue(mockCommissions);
          
          let finalRmbBalance = null;
          let finalPesoBalance = null;
          const mockInviter = {
            id: inviterId,
            balance: currency === 'RMB' ? initialBalance : 0,
            balancePeso: currency === 'PHP' ? initialBalance : 0,
            save: jest.fn().mockImplementation(function() {
              finalRmbBalance = this.balance;
              finalPesoBalance = this.balancePeso;
              return Promise.resolve();
            }),
          };
          User.findByPk.mockResolvedValue(mockInviter);
          
          // Act
          const paymentOrderService = require('../services/paymentOrderService');
          const result = await paymentOrderService.cancelPaymentOrder(orderId, 'Test cancellation');
          
          // Assert
          expect(result.commissionsCancelledCount).toBe(1);
          
          if (currency === 'RMB') {
            expect(finalRmbBalance).toBeCloseTo(initialBalance - commissionAmount, 8);
            expect(finalPesoBalance).toBe(0); // Unchanged
          } else {
            expect(finalRmbBalance).toBe(0); // Unchanged
            expect(finalPesoBalance).toBeCloseTo(initialBalance - commissionAmount, 8);
          }
        }
      ),
      { numRuns: 50 }
    );
  });
});


/**
 * Property 4: API returns all commissions for order
 * Tests for payment order query methods returning all commissions
 */
describe('Property 4: API returns all commissions for order', () => {
  /**
   * **Feature: dual-currency-commission, Property 4: API returns all commissions for order**
   * *For any* payment order with multiple commissions, the API response SHALL include all commission records
   * with their respective currencies
   * **Validates: Requirements 2.1, 2.3**
   */

  // Additional mocks needed for query methods
  jest.mock('../models/Appointment', () => ({
    findByPk: jest.fn(),
    findOne: jest.fn(),
  }));

  jest.mock('../models/Admin', () => ({
    findByPk: jest.fn(),
  }));

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('getPaymentOrderById returns all commissions', () => {
    it('should return all commission records for dual-currency orders', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 1000, noNaN: true }), // RMB commission amount
          fc.double({ min: 0.01, max: 1000, noNaN: true }), // Peso commission amount
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (rmbCommissionAmount, pesoCommissionAmount, commissionRate) => {
            // Arrange
            const orderId = 'order-uuid';
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';

            // Mock PaymentOrder.findByPk for getPaymentOrderById
            PaymentOrder.findByPk.mockResolvedValue({
              id: orderId,
              orderNo: 'PO123456789',
              userId: inviteeId,
              appointmentId: null,
              amount: null,
              amountRmb: rmbCommissionAmount / commissionRate, // Derive from commission
              amountPeso: pesoCommissionAmount / commissionRate,
              serviceContent: 'Test service',
              paymentTime: new Date(),
              notes: null,
              status: 'active',
              cancelReason: null,
              createdAt: new Date(),
              updatedAt: new Date(),
              user: {
                id: inviteeId,
                uid: '123456',
                nickname: 'Test User',
                phone: '1234567890',
                invitedBy: inviterId,
              },
              appointment: null,
              creator: {
                id: 'admin-uuid',
                username: 'admin',
                email: 'admin@test.com',
              },
            });

            // Mock Commission.findAll to return dual-currency commissions
            const mockCommissions = [
              {
                id: 'commission-rmb-uuid',
                inviterId: inviterId,
                inviteeId: inviteeId,
                paymentOrderId: orderId,
                paymentAmount: rmbCommissionAmount / commissionRate,
                commissionRate: commissionRate,
                commissionAmount: rmbCommissionAmount,
                currency: 'RMB',
                status: 'credited',
                createdAt: new Date(),
                inviter: {
                  id: inviterId,
                  uid: '654321',
                  nickname: 'Inviter User',
                },
              },
              {
                id: 'commission-php-uuid',
                inviterId: inviterId,
                inviteeId: inviteeId,
                paymentOrderId: orderId,
                paymentAmount: pesoCommissionAmount / commissionRate,
                commissionRate: commissionRate,
                commissionAmount: pesoCommissionAmount,
                currency: 'PHP',
                status: 'credited',
                createdAt: new Date(),
                inviter: {
                  id: inviterId,
                  uid: '654321',
                  nickname: 'Inviter User',
                },
              },
            ];
            Commission.findAll.mockResolvedValue(mockCommissions);

            // Act
            const paymentOrderService = require('../services/paymentOrderService');
            const result = await paymentOrderService.getPaymentOrderById(orderId);

            // Assert: Response should include commissions array with all commissions
            expect(result.commissions).not.toBeNull();
            expect(Array.isArray(result.commissions)).toBe(true);
            expect(result.commissions.length).toBe(2);

            // Verify both currencies are present
            const currencies = result.commissions.map(c => c.currency);
            expect(currencies).toContain('RMB');
            expect(currencies).toContain('PHP');

            // Verify commission amounts
            const rmbCommission = result.commissions.find(c => c.currency === 'RMB');
            const phpCommission = result.commissions.find(c => c.currency === 'PHP');
            expect(parseFloat(rmbCommission.commissionAmount)).toBeCloseTo(rmbCommissionAmount, 2);
            expect(parseFloat(phpCommission.commissionAmount)).toBeCloseTo(pesoCommissionAmount, 2);

            // Verify backward compatibility - commission field should have first commission
            expect(result.commission).not.toBeNull();
            expect(result.commission.currency).toBe('RMB'); // First commission
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should return single commission for single-currency orders', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.double({ min: 0.01, max: 1000, noNaN: true }), // commission amount
          fc.constantFrom('RMB', 'PHP'), // currency type
          fc.double({ min: 0.001, max: 0.5, noNaN: true }), // commission rate
          async (commissionAmount, currency, commissionRate) => {
            // Arrange
            const orderId = 'order-uuid';
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';

            PaymentOrder.findByPk.mockResolvedValue({
              id: orderId,
              orderNo: 'PO123456789',
              userId: inviteeId,
              appointmentId: null,
              amount: null,
              amountRmb: currency === 'RMB' ? commissionAmount / commissionRate : null,
              amountPeso: currency === 'PHP' ? commissionAmount / commissionRate : null,
              serviceContent: 'Test service',
              paymentTime: new Date(),
              notes: null,
              status: 'active',
              cancelReason: null,
              createdAt: new Date(),
              updatedAt: new Date(),
              user: {
                id: inviteeId,
                uid: '123456',
                nickname: 'Test User',
                phone: '1234567890',
                invitedBy: inviterId,
              },
              appointment: null,
              creator: {
                id: 'admin-uuid',
                username: 'admin',
                email: 'admin@test.com',
              },
            });

            // Single commission
            const mockCommissions = [
              {
                id: 'commission-uuid',
                inviterId: inviterId,
                inviteeId: inviteeId,
                paymentOrderId: orderId,
                paymentAmount: commissionAmount / commissionRate,
                commissionRate: commissionRate,
                commissionAmount: commissionAmount,
                currency: currency,
                status: 'credited',
                createdAt: new Date(),
                inviter: {
                  id: inviterId,
                  uid: '654321',
                  nickname: 'Inviter User',
                },
              },
            ];
            Commission.findAll.mockResolvedValue(mockCommissions);

            // Act
            const paymentOrderService = require('../services/paymentOrderService');
            const result = await paymentOrderService.getPaymentOrderById(orderId);

            // Assert: Response should include single commission in array
            expect(result.commissions).not.toBeNull();
            expect(Array.isArray(result.commissions)).toBe(true);
            expect(result.commissions.length).toBe(1);
            expect(result.commissions[0].currency).toBe(currency);
            expect(parseFloat(result.commissions[0].commissionAmount)).toBeCloseTo(commissionAmount, 2);

            // Backward compatibility
            expect(result.commission).not.toBeNull();
            expect(result.commission.currency).toBe(currency);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should return null commissions for orders without commissions', async () => {
      // Arrange
      const orderId = 'order-uuid';
      const inviteeId = 'invitee-uuid';

      PaymentOrder.findByPk.mockResolvedValue({
        id: orderId,
        orderNo: 'PO123456789',
        userId: inviteeId,
        appointmentId: null,
        amount: null,
        amountRmb: 100,
        amountPeso: null,
        serviceContent: 'Test service',
        paymentTime: new Date(),
        notes: null,
        status: 'active',
        cancelReason: null,
        createdAt: new Date(),
        updatedAt: new Date(),
        user: {
          id: inviteeId,
          uid: '123456',
          nickname: 'Test User',
          phone: '1234567890',
          invitedBy: null, // No inviter
        },
        appointment: null,
        creator: {
          id: 'admin-uuid',
          username: 'admin',
          email: 'admin@test.com',
        },
      });

      // No commissions
      Commission.findAll.mockResolvedValue([]);

      // Act
      const paymentOrderService = require('../services/paymentOrderService');
      const result = await paymentOrderService.getPaymentOrderById(orderId);

      // Assert: Both commissions and commission should be null
      expect(result.commissions).toBeNull();
      expect(result.commission).toBeNull();
    });
  });

  describe('getPaymentOrders returns all commissions per order', () => {
    it('should return commissions array for each order in list', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.integer({ min: 1, max: 5 }), // number of orders
          fc.double({ min: 0.01, max: 1000, noNaN: true }), // RMB commission amount
          fc.double({ min: 0.01, max: 1000, noNaN: true }), // Peso commission amount
          async (numOrders, rmbCommissionAmount, pesoCommissionAmount) => {
            // Arrange
            const orders = [];
            const allCommissions = [];

            for (let i = 0; i < numOrders; i++) {
              const orderId = `order-uuid-${i}`;
              orders.push({
                id: orderId,
                orderNo: `PO12345678${i}`,
                userId: `user-uuid-${i}`,
                appointmentId: null,
                amount: null,
                amountRmb: 100,
                amountPeso: 200,
                serviceContent: 'Test service',
                paymentTime: new Date(),
                notes: null,
                status: 'active',
                createdAt: new Date(),
                user: {
                  id: `user-uuid-${i}`,
                  uid: `12345${i}`,
                  nickname: `User ${i}`,
                  phone: '1234567890',
                },
                appointment: null,
                creator: {
                  id: 'admin-uuid',
                  username: 'admin',
                  email: 'admin@test.com',
                },
              });

              // Add dual-currency commissions for each order
              allCommissions.push({
                paymentOrderId: orderId,
                commissionAmount: rmbCommissionAmount,
                status: 'credited',
                currency: 'RMB',
              });
              allCommissions.push({
                paymentOrderId: orderId,
                commissionAmount: pesoCommissionAmount,
                status: 'credited',
                currency: 'PHP',
              });
            }

            // Mock PaymentOrder.findAndCountAll
            PaymentOrder.findAndCountAll = jest.fn().mockResolvedValue({
              count: numOrders,
              rows: orders,
            });

            // Mock PaymentOrder.findOne for statistics
            PaymentOrder.findOne = jest.fn().mockResolvedValue({
              totalRmb: 100 * numOrders,
              totalPeso: 200 * numOrders,
            });

            // Mock Commission.findAll for all orders
            Commission.findAll.mockResolvedValue(allCommissions);

            // Act
            const paymentOrderService = require('../services/paymentOrderService');
            const result = await paymentOrderService.getPaymentOrders({ page: 1, limit: 20 });

            // Assert: Each order should have commissions array
            expect(result.records.length).toBe(numOrders);
            
            for (const record of result.records) {
              expect(record.commissions).not.toBeNull();
              expect(Array.isArray(record.commissions)).toBe(true);
              expect(record.commissions.length).toBe(2);

              // Verify both currencies present
              const currencies = record.commissions.map(c => c.currency);
              expect(currencies).toContain('RMB');
              expect(currencies).toContain('PHP');

              // Verify backward compatibility
              expect(record.commission).not.toBeNull();
            }
          }
        ),
        { numRuns: 50 }
      );
    });

    it('should return null commissions for orders without commissions in list', async () => {
      // Arrange
      const orders = [
        {
          id: 'order-uuid-1',
          orderNo: 'PO123456781',
          userId: 'user-uuid-1',
          appointmentId: null,
          amount: null,
          amountRmb: 100,
          amountPeso: null,
          serviceContent: 'Test service',
          paymentTime: new Date(),
          notes: null,
          status: 'active',
          createdAt: new Date(),
          user: {
            id: 'user-uuid-1',
            uid: '123451',
            nickname: 'User 1',
            phone: '1234567890',
          },
          appointment: null,
          creator: {
            id: 'admin-uuid',
            username: 'admin',
            email: 'admin@test.com',
          },
        },
      ];

      PaymentOrder.findAndCountAll = jest.fn().mockResolvedValue({
        count: 1,
        rows: orders,
      });

      PaymentOrder.findOne = jest.fn().mockResolvedValue({
        totalRmb: 100,
        totalPeso: 0,
      });

      // No commissions
      Commission.findAll.mockResolvedValue([]);

      // Act
      const paymentOrderService = require('../services/paymentOrderService');
      const result = await paymentOrderService.getPaymentOrders({ page: 1, limit: 20 });

      // Assert
      expect(result.records.length).toBe(1);
      expect(result.records[0].commissions).toBeNull();
      expect(result.records[0].commission).toBeNull();
    });
  });
});


/**
 * Property 5: Statistics aggregate by currency
 * Tests for commission statistics aggregation by currency
 */
describe('Property 5: Statistics aggregate by currency', () => {
  /**
   * **Feature: dual-currency-commission, Property 5: Statistics aggregate by currency**
   * *For any* commission statistics query, the totals SHALL be correctly separated by currency
   * (totalCommissionRmb, totalCommissionPeso)
   * **Validates: Requirements 4.1, 4.2, 4.3**
   */

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('getCommissionStats aggregates by currency', () => {
    it('should correctly aggregate commission totals by currency', async () => {
      await fc.assert(
        fc.asyncProperty(
          // Generate array of commissions with random amounts and currencies
          fc.array(
            fc.record({
              commissionAmount: fc.double({ min: 0.01, max: 1000, noNaN: true }),
              currency: fc.constantFrom('RMB', 'PHP'),
            }),
            { minLength: 1, maxLength: 20 }
          ),
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial RMB balance
          fc.double({ min: 0, max: 10000, noNaN: true }), // initial Peso balance
          async (commissions, initialBalance, initialBalancePeso) => {
            // Arrange
            const inviterId = 'inviter-uuid';

            // Calculate expected totals
            let expectedRmbTotal = 0;
            let expectedPesoTotal = 0;
            commissions.forEach(c => {
              if (c.currency === 'PHP') {
                expectedPesoTotal += c.commissionAmount;
              } else {
                expectedRmbTotal += c.commissionAmount;
              }
            });

            // Mock User.findByPk
            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: initialBalance,
              balancePeso: initialBalancePeso,
              invitationCode: 'ABC123',
              update: jest.fn().mockResolvedValue(true),
            });

            // Mock User.count for total invites
            User.count.mockResolvedValue(5);

            // Mock Commission.findAll to return the generated commissions
            Commission.findAll.mockResolvedValue(
              commissions.map((c, i) => ({
                id: `commission-uuid-${i}`,
                commissionAmount: c.commissionAmount,
                currency: c.currency,
                status: 'credited',
              }))
            );

            // Mock Commission.count for paid invites
            Commission.count.mockResolvedValue(3);

            // Act
            const result = await commissionService.getCommissionStats(inviterId);

            // Assert: Totals should be correctly separated by currency
            expect(parseFloat(result.totalCommissionRmb)).toBeCloseTo(expectedRmbTotal, 2);
            expect(parseFloat(result.totalCommissionPeso)).toBeCloseTo(expectedPesoTotal, 2);

            // Verify balances are returned correctly
            expect(parseFloat(result.availableBalanceRmb)).toBeCloseTo(initialBalance, 2);
            expect(parseFloat(result.availableBalancePeso)).toBeCloseTo(initialBalancePeso, 2);
          }
        ),
        { numRuns: 100 }
      );
    });

    it('should return zero totals when no commissions exist', async () => {
      // Arrange
      const inviterId = 'inviter-uuid';

      User.findByPk.mockResolvedValue({
        id: inviterId,
        balance: 100,
        balancePeso: 200,
        invitationCode: 'ABC123',
        update: jest.fn().mockResolvedValue(true),
      });

      User.count.mockResolvedValue(0);
      Commission.findAll.mockResolvedValue([]);
      Commission.count.mockResolvedValue(0);

      // Act
      const result = await commissionService.getCommissionStats(inviterId);

      // Assert
      expect(parseFloat(result.totalCommissionRmb)).toBe(0);
      expect(parseFloat(result.totalCommissionPeso)).toBe(0);
    });

    it('should handle only RMB commissions correctly', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.array(
            fc.double({ min: 0.01, max: 1000, noNaN: true }),
            { minLength: 1, maxLength: 10 }
          ),
          async (rmbAmounts) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const expectedRmbTotal = rmbAmounts.reduce((sum, amt) => sum + amt, 0);

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 100,
              balancePeso: 0,
              invitationCode: 'ABC123',
              update: jest.fn().mockResolvedValue(true),
            });

            User.count.mockResolvedValue(5);
            Commission.findAll.mockResolvedValue(
              rmbAmounts.map((amt, i) => ({
                id: `commission-uuid-${i}`,
                commissionAmount: amt,
                currency: 'RMB',
                status: 'credited',
              }))
            );
            Commission.count.mockResolvedValue(3);

            // Act
            const result = await commissionService.getCommissionStats(inviterId);

            // Assert
            expect(parseFloat(result.totalCommissionRmb)).toBeCloseTo(expectedRmbTotal, 2);
            expect(parseFloat(result.totalCommissionPeso)).toBe(0);
          }
        ),
        { numRuns: 50 }
      );
    });

    it('should handle only PHP commissions correctly', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.array(
            fc.double({ min: 0.01, max: 1000, noNaN: true }),
            { minLength: 1, maxLength: 10 }
          ),
          async (pesoAmounts) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const expectedPesoTotal = pesoAmounts.reduce((sum, amt) => sum + amt, 0);

            User.findByPk.mockResolvedValue({
              id: inviterId,
              balance: 0,
              balancePeso: 100,
              invitationCode: 'ABC123',
              update: jest.fn().mockResolvedValue(true),
            });

            User.count.mockResolvedValue(5);
            Commission.findAll.mockResolvedValue(
              pesoAmounts.map((amt, i) => ({
                id: `commission-uuid-${i}`,
                commissionAmount: amt,
                currency: 'PHP',
                status: 'credited',
              }))
            );
            Commission.count.mockResolvedValue(3);

            // Act
            const result = await commissionService.getCommissionStats(inviterId);

            // Assert
            expect(parseFloat(result.totalCommissionRmb)).toBe(0);
            expect(parseFloat(result.totalCommissionPeso)).toBeCloseTo(expectedPesoTotal, 2);
          }
        ),
        { numRuns: 50 }
      );
    });
  });

  describe('getInvitedUsersWithCommissions aggregates by currency per invitee', () => {
    it('should correctly aggregate commission totals by currency for each invitee', async () => {
      await fc.assert(
        fc.asyncProperty(
          // Generate array of commissions with random amounts and currencies
          fc.array(
            fc.record({
              commissionAmount: fc.double({ min: 0.01, max: 1000, noNaN: true }),
              paymentAmount: fc.double({ min: 1, max: 10000, noNaN: true }),
              currency: fc.constantFrom('RMB', 'PHP'),
            }),
            { minLength: 1, maxLength: 10 }
          ),
          async (commissions) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const inviteeId = 'invitee-uuid';

            // Calculate expected totals
            let expectedRmbCommission = 0;
            let expectedPesoCommission = 0;
            let expectedRmbPayment = 0;
            let expectedPesoPayment = 0;

            commissions.forEach(c => {
              if (c.currency === 'PHP') {
                expectedPesoCommission += c.commissionAmount;
                expectedPesoPayment += c.paymentAmount;
              } else {
                expectedRmbCommission += c.commissionAmount;
                expectedRmbPayment += c.paymentAmount;
              }
            });

            // Mock User.findAndCountAll for invited users
            User.findAndCountAll = jest.fn().mockResolvedValue({
              count: 1,
              rows: [{
                id: inviteeId,
                uid: '123456',
                nickname: 'Test Invitee',
                avatar: null,
                createdAt: new Date(),
              }],
            });

            // Mock Commission.findAll for the invitee's commissions
            Commission.findAll.mockResolvedValue(
              commissions.map((c, i) => ({
                id: `commission-uuid-${i}`,
                inviterId: inviterId,
                inviteeId: inviteeId,
                commissionAmount: c.commissionAmount,
                paymentAmount: c.paymentAmount,
                currency: c.currency,
                status: 'credited',
                createdAt: new Date(),
                paymentOrder: {
                  id: `order-uuid-${i}`,
                  orderNo: `PO12345678${i}`,
                  serviceContent: 'Test service',
                  paymentTime: new Date(),
                  amountRmb: c.currency === 'RMB' ? c.paymentAmount : null,
                  amountPeso: c.currency === 'PHP' ? c.paymentAmount : null,
                },
              }))
            );

            // Act
            const result = await commissionService.getInvitedUsersWithCommissions(inviterId, { page: 1, limit: 20 });

            // Assert: Totals should be correctly separated by currency
            expect(result.records.length).toBe(1);
            const inviteeRecord = result.records[0];

            expect(parseFloat(inviteeRecord.totalCommissionRmb)).toBeCloseTo(expectedRmbCommission, 2);
            expect(parseFloat(inviteeRecord.totalCommissionPeso)).toBeCloseTo(expectedPesoCommission, 2);
            expect(parseFloat(inviteeRecord.totalPaymentRmb)).toBeCloseTo(expectedRmbPayment, 2);
            expect(parseFloat(inviteeRecord.totalPaymentPeso)).toBeCloseTo(expectedPesoPayment, 2);

            // Verify order count
            expect(inviteeRecord.orderCount).toBe(commissions.length);

            // Verify each order has correct currency
            inviteeRecord.orders.forEach((order, i) => {
              expect(order.currency).toBe(commissions[i].currency);
            });
          }
        ),
        { numRuns: 50 }
      );
    });

    it('should return zero totals for invitee with no commissions', async () => {
      // Arrange
      const inviterId = 'inviter-uuid';
      const inviteeId = 'invitee-uuid';

      User.findAndCountAll = jest.fn().mockResolvedValue({
        count: 1,
        rows: [{
          id: inviteeId,
          uid: '123456',
          nickname: 'Test Invitee',
          avatar: null,
          createdAt: new Date(),
        }],
      });

      Commission.findAll.mockResolvedValue([]);

      // Act
      const result = await commissionService.getInvitedUsersWithCommissions(inviterId, { page: 1, limit: 20 });

      // Assert
      expect(result.records.length).toBe(1);
      const inviteeRecord = result.records[0];

      expect(parseFloat(inviteeRecord.totalCommissionRmb)).toBe(0);
      expect(parseFloat(inviteeRecord.totalCommissionPeso)).toBe(0);
      expect(parseFloat(inviteeRecord.totalPaymentRmb)).toBe(0);
      expect(parseFloat(inviteeRecord.totalPaymentPeso)).toBe(0);
      expect(inviteeRecord.orderCount).toBe(0);
    });

    it('should handle multiple invitees with different currency distributions', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.integer({ min: 2, max: 5 }), // number of invitees
          async (numInvitees) => {
            // Arrange
            const inviterId = 'inviter-uuid';
            const invitees = [];
            const commissionsByInvitee = new Map();

            for (let i = 0; i < numInvitees; i++) {
              const inviteeId = `invitee-uuid-${i}`;
              invitees.push({
                id: inviteeId,
                uid: `12345${i}`,
                nickname: `Invitee ${i}`,
                avatar: null,
                createdAt: new Date(),
              });

              // Generate random commissions for this invitee
              const numCommissions = Math.floor(Math.random() * 5) + 1;
              const commissions = [];
              for (let j = 0; j < numCommissions; j++) {
                commissions.push({
                  id: `commission-uuid-${i}-${j}`,
                  inviterId: inviterId,
                  inviteeId: inviteeId,
                  commissionAmount: Math.random() * 100,
                  paymentAmount: Math.random() * 1000,
                  currency: Math.random() > 0.5 ? 'RMB' : 'PHP',
                  status: 'credited',
                  createdAt: new Date(),
                  paymentOrder: {
                    id: `order-uuid-${i}-${j}`,
                    orderNo: `PO${i}${j}`,
                    serviceContent: 'Test service',
                    paymentTime: new Date(),
                  },
                });
              }
              commissionsByInvitee.set(inviteeId, commissions);
            }

            User.findAndCountAll = jest.fn().mockResolvedValue({
              count: numInvitees,
              rows: invitees,
            });

            // Mock Commission.findAll to return commissions for the specific invitee
            Commission.findAll.mockImplementation(({ where }) => {
              const inviteeId = where.inviteeId;
              return Promise.resolve(commissionsByInvitee.get(inviteeId) || []);
            });

            // Act
            const result = await commissionService.getInvitedUsersWithCommissions(inviterId, { page: 1, limit: 20 });

            // Assert: Each invitee should have correct totals
            expect(result.records.length).toBe(numInvitees);

            for (let i = 0; i < numInvitees; i++) {
              const inviteeRecord = result.records[i];
              const inviteeId = invitees[i].id;
              const commissions = commissionsByInvitee.get(inviteeId);

              // Calculate expected totals
              let expectedRmbCommission = 0;
              let expectedPesoCommission = 0;
              commissions.forEach(c => {
                if (c.currency === 'PHP') {
                  expectedPesoCommission += c.commissionAmount;
                } else {
                  expectedRmbCommission += c.commissionAmount;
                }
              });

              expect(parseFloat(inviteeRecord.totalCommissionRmb)).toBeCloseTo(expectedRmbCommission, 2);
              expect(parseFloat(inviteeRecord.totalCommissionPeso)).toBeCloseTo(expectedPesoCommission, 2);
            }
          }
        ),
        { numRuns: 30 }
      );
    });
  });
});
