import { describe, expect, it } from 'vitest';
import { aggregateTransactions, } from '../providers/transaction-aggregator.js';
describe('Transaction Aggregator', () => {
    // Helper to create test transactions
    const createTransaction = (overrides = {}) => ({
        id: 'txn-' + Math.random().toString(36).substr(2, 9),
        charge_id: 'charge-123',
        amount: "100",
        currency: 'USD',
        business_id: null,
        event_date: new Date('2024-01-15'),
        debit_date: null,
        source_description: 'Test transaction',
        is_fee: false,
        debit_timestamp: null,
        ...overrides,
    });
    describe('Single Transaction', () => {
        it('should return single transaction as-is', () => {
            const transaction = createTransaction({
                amount: "150.5",
                currency: 'USD',
                business_id: 'business-1',
                event_date: new Date('2024-01-15'),
                source_description: 'Payment from client',
            });
            const result = aggregateTransactions([transaction]);
            expect(result).toEqual({
                amount: 150.5,
                currency: 'USD',
                businessId: 'business-1',
                date: new Date('2024-01-15'),
                debitDate: null,
                description: 'Payment from client',
            });
        });
        it('should handle transaction with null business_id', () => {
            const transaction = createTransaction({
                amount: "200",
                business_id: null,
            });
            const result = aggregateTransactions([transaction]);
            expect(result.businessId).toBeNull();
        });
        it('should handle transaction with null description', () => {
            const transaction = createTransaction({
                source_description: null,
            });
            const result = aggregateTransactions([transaction]);
            expect(result.description).toBe('');
        });
        it('should handle transaction with empty description', () => {
            const transaction = createTransaction({
                source_description: '   ',
            });
            const result = aggregateTransactions([transaction]);
            expect(result.description).toBe('');
        });
    });
    describe('Multiple Transactions - Same Currency', () => {
        it('should sum amounts correctly', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD' }),
                createTransaction({ amount: "200", currency: 'USD' }),
                createTransaction({ amount: "50.5", currency: 'USD' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(350.5);
            expect(result.currency).toBe('USD');
        });
        it('should handle negative amounts (debits)', () => {
            const transactions = [
                createTransaction({ amount: "-100", currency: 'ILS' }),
                createTransaction({ amount: "-50", currency: 'ILS' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(-150);
            expect(result.currency).toBe('ILS');
        });
        it('should handle mixed positive and negative amounts', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'EUR' }),
                createTransaction({ amount: "-30", currency: 'EUR' }),
                createTransaction({ amount: "50", currency: 'EUR' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(120);
        });
    });
    describe('Fee Transactions', () => {
        it('should exclude transactions where is_fee is true', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD', is_fee: false }),
                createTransaction({ amount: "5", currency: 'USD', is_fee: true }), // Fee - should be excluded
                createTransaction({ amount: "200", currency: 'USD', is_fee: false }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(300); // 100 + 200, fee excluded
        });
        it('should exclude transactions where is_fee is null but treated as false', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD', is_fee: null }),
                createTransaction({ amount: "50", currency: 'USD', is_fee: false }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(150); // Both included (null treated as false)
        });
        it('should throw error when all transactions are fees', () => {
            const transactions = [
                createTransaction({ amount: "5", is_fee: true }),
                createTransaction({ amount: "3", is_fee: true }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow('Cannot aggregate transactions: all transactions are marked as fees');
        });
        it('should not include fee descriptions in concatenation', () => {
            const transactions = [
                createTransaction({
                    amount: "100",
                    source_description: 'Main transaction',
                    is_fee: false,
                }),
                createTransaction({ amount: "5", source_description: 'Bank fee', is_fee: true }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('Main transaction');
        });
    });
    describe('Currency Validation', () => {
        it('should throw error with multiple currencies', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD' }),
                createTransaction({ amount: "200", currency: 'EUR' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow('Cannot aggregate transactions: multiple currencies found (USD, EUR)');
        });
        it('should throw error with three different currencies', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD' }),
                createTransaction({ amount: "200", currency: 'EUR' }),
                createTransaction({ amount: "300", currency: 'GBP' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow(/multiple currencies found/);
        });
        it('should not throw error when all have same currency', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'ILS' }),
                createTransaction({ amount: "200", currency: 'ILS' }),
                createTransaction({ amount: "300", currency: 'ILS' }),
            ];
            expect(() => aggregateTransactions(transactions)).not.toThrow();
        });
        it('should ignore currency of fee transactions in validation', () => {
            const transactions = [
                createTransaction({ amount: "100", currency: 'USD', is_fee: false }),
                createTransaction({ amount: "5", currency: 'EUR', is_fee: true }), // Different currency but is fee
                createTransaction({ amount: "200", currency: 'USD', is_fee: false }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.currency).toBe('USD');
            expect(result.amount).toBe(300);
        });
    });
    describe('Business ID Validation', () => {
        it('should throw error with multiple different business IDs', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: 'business-1' }),
                createTransaction({ amount: "200", business_id: 'business-2' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow('Cannot aggregate transactions: multiple business IDs found (business-1, business-2)');
        });
        it('should throw error with three different business IDs', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: 'business-1' }),
                createTransaction({ amount: "200", business_id: 'business-2' }),
                createTransaction({ amount: "300", business_id: 'business-3' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow(/multiple business IDs found/);
        });
        it('should return null when all business IDs are null', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: null }),
                createTransaction({ amount: "200", business_id: null }),
                createTransaction({ amount: "300", business_id: null }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.businessId).toBeNull();
        });
        it('should return single non-null business ID', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: 'business-1' }),
                createTransaction({ amount: "200", business_id: null }),
                createTransaction({ amount: "300", business_id: null }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.businessId).toBe('business-1');
        });
        it('should handle multiple nulls and one business ID', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: null }),
                createTransaction({ amount: "200", business_id: 'business-xyz' }),
                createTransaction({ amount: "300", business_id: null }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.businessId).toBe('business-xyz');
        });
        it('should not throw when all have same business ID', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: 'business-1' }),
                createTransaction({ amount: "200", business_id: 'business-1' }),
                createTransaction({ amount: "300", business_id: 'business-1' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.businessId).toBe('business-1');
        });
        it('should ignore business ID of fee transactions in validation', () => {
            const transactions = [
                createTransaction({ amount: "100", business_id: 'business-1', is_fee: false }),
                createTransaction({ amount: "5", business_id: 'business-2', is_fee: true }), // Different business but is fee
                createTransaction({ amount: "200", business_id: 'business-1', is_fee: false }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.businessId).toBe('business-1');
        });
    });
    describe('Date Selection', () => {
        it('should select earliest event_date', () => {
            const transactions = [
                createTransaction({ event_date: new Date('2024-03-15') }),
                createTransaction({ event_date: new Date('2024-01-10') }), // Earliest
                createTransaction({ event_date: new Date('2024-02-20') }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.date).toEqual(new Date('2024-01-10'));
        });
        it('should select earliest from same month', () => {
            const transactions = [
                createTransaction({ event_date: new Date('2024-01-20') }),
                createTransaction({ event_date: new Date('2024-01-15') }), // Earliest
                createTransaction({ event_date: new Date('2024-01-25') }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.date).toEqual(new Date('2024-01-15'));
        });
        it('should handle dates spanning years', () => {
            const transactions = [
                createTransaction({ event_date: new Date('2024-01-15') }),
                createTransaction({ event_date: new Date('2023-12-31') }), // Earliest (previous year)
                createTransaction({ event_date: new Date('2024-02-01') }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.date).toEqual(new Date('2023-12-31'));
        });
        it('should handle same date for all transactions', () => {
            const transactions = [
                createTransaction({ event_date: new Date('2024-01-15') }),
                createTransaction({ event_date: new Date('2024-01-15') }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.date).toEqual(new Date('2024-01-15'));
        });
    });
    describe('Description Concatenation', () => {
        it('should concatenate descriptions with line breaks', () => {
            const transactions = [
                createTransaction({ source_description: 'First transaction' }),
                createTransaction({ source_description: 'Second transaction' }),
                createTransaction({ source_description: 'Third transaction' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('First transaction\nSecond transaction\nThird transaction');
        });
        it('should filter out null descriptions', () => {
            const transactions = [
                createTransaction({ source_description: 'First' }),
                createTransaction({ source_description: null }),
                createTransaction({ source_description: 'Third' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('First\nThird');
        });
        it('should filter out empty string descriptions', () => {
            const transactions = [
                createTransaction({ source_description: 'First' }),
                createTransaction({ source_description: '   ' }),
                createTransaction({ source_description: 'Third' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('First\nThird');
        });
        it('should return empty string when all descriptions are null', () => {
            const transactions = [
                createTransaction({ source_description: null }),
                createTransaction({ source_description: null }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('');
        });
        it('should preserve order of descriptions', () => {
            const transactions = [
                createTransaction({ source_description: 'Alpha' }),
                createTransaction({ source_description: 'Beta' }),
                createTransaction({ source_description: 'Gamma' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('Alpha\nBeta\nGamma');
        });
        it('should handle special characters in descriptions', () => {
            const transactions = [
                createTransaction({ source_description: 'Payment: $100 USD' }),
                createTransaction({ source_description: 'Ref #12345 (invoice)' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.description).toBe('Payment: $100 USD\nRef #12345 (invoice)');
        });
    });
    describe('Input Validation', () => {
        it('should throw error for empty array', () => {
            expect(() => aggregateTransactions([])).toThrow('Cannot aggregate transactions: array is empty');
        });
        it('should throw error for null input', () => {
            expect(() => aggregateTransactions(null)).toThrow('Cannot aggregate transactions: array is empty');
        });
        it('should throw error for undefined input', () => {
            expect(() => aggregateTransactions(undefined)).toThrow('Cannot aggregate transactions: array is empty');
        });
    });
    describe('Complex Scenarios', () => {
        it('should handle real-world scenario with fees, nulls, and mixed data', () => {
            const transactions = [
                createTransaction({
                    amount: "1000",
                    currency: 'USD',
                    business_id: 'client-abc',
                    event_date: new Date('2024-01-15'),
                    source_description: 'Invoice #123 payment',
                    is_fee: false,
                }),
                createTransaction({
                    amount: "2.5",
                    currency: 'USD',
                    business_id: 'bank-xyz',
                    event_date: new Date('2024-01-15'),
                    source_description: 'Wire transfer fee',
                    is_fee: true, // Should be excluded
                }),
                createTransaction({
                    amount: "500",
                    currency: 'USD',
                    business_id: 'client-abc',
                    event_date: new Date('2024-01-10'), // Earlier date
                    source_description: 'Partial payment',
                    is_fee: false,
                }),
                createTransaction({
                    amount: "200",
                    currency: 'USD',
                    business_id: null, // Null business
                    event_date: new Date('2024-01-20'),
                    source_description: null, // Null description
                    is_fee: false,
                }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(1700); // 1000 + 500 + 200 (fee excluded)
            expect(result.currency).toBe('USD');
            expect(result.businessId).toBe('client-abc'); // One non-null, one null
            expect(result.date).toEqual(new Date('2024-01-10')); // Earliest
            expect(result.description).toBe('Invoice #123 payment\nPartial payment'); // Fee desc excluded, null excluded
        });
        it('should handle cryptocurrency transactions', () => {
            const transactions = [
                createTransaction({ amount: "0.5", currency: 'ETH' }),
                createTransaction({ amount: "0.3", currency: 'ETH' }),
            ];
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(0.8);
            expect(result.currency).toBe('ETH');
        });
        it('should handle large number of transactions', () => {
            const transactions = Array.from({ length: 100 }, (_, i) => createTransaction({
                amount: "10",
                currency: 'USD',
                event_date: new Date(`2024-01-${(i % 28) + 1}`),
            }));
            const result = aggregateTransactions(transactions);
            expect(result.amount).toBe(1000); // 100 × 10
            expect(result.currency).toBe('USD');
        });
    });
    describe('Error Messages', () => {
        it('should provide descriptive error for mixed currencies', () => {
            const transactions = [
                createTransaction({ currency: 'USD' }),
                createTransaction({ currency: 'EUR' }),
                createTransaction({ currency: 'GBP' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow(/multiple currencies found \(USD, EUR, GBP\)/);
        });
        it('should provide descriptive error for multiple business IDs', () => {
            const transactions = [
                createTransaction({ business_id: 'business-a' }),
                createTransaction({ business_id: 'business-b' }),
            ];
            expect(() => aggregateTransactions(transactions)).toThrow(/multiple business IDs found \(business-a, business-b\)/);
        });
        it('should provide descriptive error for all fees', () => {
            const transactions = [createTransaction({ is_fee: true })];
            expect(() => aggregateTransactions(transactions)).toThrow('all transactions are marked as fees');
        });
        it('should provide descriptive error for empty array', () => {
            expect(() => aggregateTransactions([])).toThrow('array is empty');
        });
    });
});
//# sourceMappingURL=transaction-aggregator.test.js.map