import { describe, expect, it, vi, beforeEach } from 'vitest';
import { createMockTransaction, createMockDocument } from './test-helpers.js';
// Mock the module imports
vi.mock('graphql-modules', () => ({
    Injectable: () => (target) => target,
    Inject: () => (target, propertyKey, parameterIndex) => { },
    Injector: class {
    },
    InjectionToken: class {
        description;
        constructor(description) {
            this.description = description;
        }
    },
    Scope: { Operation: 'Operation' },
    CONTEXT: Symbol('CONTEXT'),
}));
vi.mock('../../charges/providers/charges.provider.js', () => ({
    ChargesProvider: class {
    },
}));
vi.mock('../../documents/providers/documents.provider.js', () => ({
    DocumentsProvider: class {
    },
}));
vi.mock('../../transactions/providers/transactions.provider.js', () => ({
    TransactionsProvider: class {
    },
}));
vi.mock('../../financial-entities/providers/clients.provider.js', () => ({
    ClientsProvider: class {
    },
}));
vi.mock('../../admin-context/providers/admin-context.provider.js', () => ({
    AdminContextProvider: class {
    },
}));
vi.mock('../../charges/helpers/merge-charges.helper.js', () => ({
    mergeChargesExecutor: vi.fn(),
}));
vi.mock('../../../shared/helpers/index.js', () => ({
    dateToTimelessDateString: (date) => date.toISOString().split('T')[0],
}));
const createMockProvider = (mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider) => {
    const mockAdminContextProvider = {
        getVerifiedAdminContext: vi.fn(() => Promise.resolve({ ownerId: ADMIN_BUSINESS_ID })),
    };
    const mockCharges = mockChargesProvider ?? {
        getChargesByFilters: vi.fn(() => Promise.resolve([])),
    };
    const mockTransactions = mockTransactionsProvider ?? {
        transactionsByChargeIDLoader: {
            load: vi.fn(() => Promise.resolve([])),
        },
    };
    const mockDocuments = mockDocumentsProvider ?? {
        getDocumentsByChargeIdLoader: {
            load: vi.fn(() => Promise.resolve([])),
        },
    };
    const mockInjector = {
        get: vi.fn((token) => {
            if (token.name === 'ChargesProvider')
                return mockCharges;
            if (token.name === 'TransactionsProvider')
                return mockTransactions;
            if (token.name === 'DocumentsProvider')
                return mockDocuments;
            if (token.name === 'ClientsProvider')
                return {
                    getClientByIdLoader: {
                        load: (businessId) => {
                            const isRegisteredClient = businessId.startsWith('client-');
                            return Promise.resolve(isRegisteredClient ? { id: businessId } : null);
                        },
                    },
                };
            return null;
        }),
    };
    const mockContext = {
        injector: mockInjector,
    };
    return new ChargesMatcherProvider(mockAdminContextProvider, mockCharges, mockTransactions, mockDocuments, mockContext);
};
// Import after mocking
const { ChargesMatcherProvider } = await import('../providers/charges-matcher.provider.js');
const { mergeChargesExecutor } = await import('../../charges/helpers/merge-charges.helper.js');
// Test constants
const ADMIN_BUSINESS_ID = 'user-123';
// Helper to create charge
function createCharge(id, ownerId) {
    return {
        id,
        owner_id: ownerId,
    };
}
describe('ChargesMatcherProvider - Auto-Match Integration', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });
    describe('autoMatchCharges', () => {
        it('should return 0 matches when database is empty', async () => {
            const provider = createMockProvider();
            const result = await provider.autoMatchCharges();
            expect(result.totalMatches).toBe(0);
            expect(result.mergedCharges).toEqual([]);
            expect(result.skippedCharges).toEqual([]);
            expect(result.errors).toEqual([]);
        });
        it('should return 0 matches when all charges are already matched', async () => {
            const matchedChargeId = 'matched-charge-1';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([createCharge(matchedChargeId, ADMIN_BUSINESS_ID)])),
            };
            const provider = createMockProvider(mockChargesProvider);
            const result = await provider.autoMatchCharges();
            expect(result.totalMatches).toBe(0);
            expect(result.mergedCharges).toEqual([]);
            expect(result.skippedCharges).toEqual([]);
        });
        it('should execute merge for single unmatched charge with good match', async () => {
            const txChargeId = 'tx-charge-1';
            const docChargeId = 'doc-charge-1';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(txChargeId, ADMIN_BUSINESS_ID),
                    createCharge(docChargeId, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === txChargeId) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: txChargeId,
                                    amount: "100",
                                    currency: 'USD',
                                    event_date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === docChargeId) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: docChargeId,
                                    total_amount: 100,
                                    currency_code: 'USD',
                                    date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            expect(result.totalMatches).toBe(1);
            expect(result.mergedCharges).toHaveLength(1);
            expect(result.mergedCharges[0].confidenceScore).toBeGreaterThanOrEqual(0.95);
            expect(result.skippedCharges).toEqual([]);
            expect(result.errors).toEqual([]);
            // Verify merge was called
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(1);
        });
        it('should skip charges with ambiguous matches', async () => {
            const txChargeId = 'tx-charge-1';
            const docCharge1Id = 'doc-charge-1';
            const docCharge2Id = 'doc-charge-2';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(txChargeId, ADMIN_BUSINESS_ID),
                    createCharge(docCharge1Id, ADMIN_BUSINESS_ID),
                    createCharge(docCharge2Id, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === txChargeId) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: txChargeId,
                                    amount: "100",
                                    currency: 'USD',
                                    event_date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === docCharge1Id) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: docCharge1Id,
                                    total_amount: 100,
                                    currency_code: 'USD',
                                    date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        if (id === docCharge2Id) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: docCharge2Id,
                                    total_amount: 100,
                                    currency_code: 'USD',
                                    date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            // The transaction charge should find 2 identical doc matches and skip (ambiguous)
            // But the doc charges will also match each other with high confidence
            // Since processing is sequential, one doc will match the other doc
            // So we expect: 1 match (doc1-doc2), and tx skipped due to ambiguity
            expect(result.totalMatches).toBe(1);
            expect(result.skippedCharges).toHaveLength(1);
            expect(result.skippedCharges[0]).toBe(txChargeId);
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(1);
        });
        it('should process multiple unmatched charges correctly', async () => {
            const tx1Id = 'tx-charge-1';
            const doc1Id = 'doc-charge-1';
            const tx2Id = 'tx-charge-2';
            const doc2Id = 'doc-charge-2';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(tx1Id, ADMIN_BUSINESS_ID),
                    createCharge(doc1Id, ADMIN_BUSINESS_ID),
                    createCharge(tx2Id, ADMIN_BUSINESS_ID),
                    createCharge(doc2Id, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === tx1Id) {
                            return Promise.resolve([
                                createMockTransaction({ charge_id: tx1Id, amount: "100", currency: 'USD' }),
                            ]);
                        }
                        if (id === tx2Id) {
                            return Promise.resolve([
                                createMockTransaction({ charge_id: tx2Id, amount: "200", currency: 'EUR' }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === doc1Id) {
                            return Promise.resolve([
                                createMockDocument({ charge_id: doc1Id, total_amount: 100, currency_code: 'USD' }),
                            ]);
                        }
                        if (id === doc2Id) {
                            return Promise.resolve([
                                createMockDocument({ charge_id: doc2Id, total_amount: 200, currency_code: 'EUR' }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            expect(result.totalMatches).toBe(2);
            expect(result.mergedCharges).toHaveLength(2);
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(2);
        });
        it('should capture errors during merge but continue processing', async () => {
            const tx1Id = 'tx-charge-1';
            const doc1Id = 'doc-charge-1';
            const tx2Id = 'tx-charge-2'; // This will have no match
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(tx1Id, ADMIN_BUSINESS_ID),
                    createCharge(doc1Id, ADMIN_BUSINESS_ID),
                    createCharge(tx2Id, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === tx1Id) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: id,
                                    amount: "100",
                                    currency: 'USD',
                                    event_date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        if (id === tx2Id) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: id,
                                    amount: "999",
                                    currency: 'GBP',
                                    event_date: new Date('2024-03-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === doc1Id) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: id,
                                    total_amount: 100,
                                    currency_code: 'USD',
                                    date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            // Mock merge to fail on first call, succeed on second
            mergeChargesExecutor.mockImplementationOnce(() => {
                throw new Error('Merge failed for test');
            });
            mergeChargesExecutor.mockImplementationOnce(() => Promise.resolve());
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            // tx1 finds doc1, merge fails → error captured, charges not marked as merged
            // doc1 finds tx1, merge succeeds → totalMatches++, charges marked as merged
            // tx2 has no match → silent
            expect(result.totalMatches).toBe(1);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0]).toContain('Failed to merge');
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(2);
        });
        it('should verify merge direction keeps transaction charge', async () => {
            const txChargeId = 'tx-charge-1';
            const docChargeId = 'doc-charge-1';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(txChargeId, ADMIN_BUSINESS_ID),
                    createCharge(docChargeId, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === txChargeId) {
                            return Promise.resolve([createMockTransaction({ charge_id: txChargeId })]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === docChargeId) {
                            return Promise.resolve([createMockDocument({ charge_id: docChargeId })]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            await provider.autoMatchCharges();
            // Verify merge was called with doc charge being merged into tx charge
            expect(mergeChargesExecutor).toHaveBeenCalledWith([docChargeId], txChargeId, expect.any(Object));
        });
        it('should exclude merged charges from further processing in same run', async () => {
            const tx1Id = 'tx-charge-1';
            const doc1Id = 'doc-charge-1';
            const tx2Id = 'tx-charge-2';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(tx1Id, ADMIN_BUSINESS_ID),
                    createCharge(doc1Id, ADMIN_BUSINESS_ID),
                    createCharge(tx2Id, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === tx1Id || id === tx2Id) {
                            return Promise.resolve([createMockTransaction({ charge_id: id, amount: "100" })]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === doc1Id) {
                            return Promise.resolve([createMockDocument({ charge_id: doc1Id, total_amount: 100 })]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            // Should only merge tx1 with doc1, tx2 should have no match
            expect(result.totalMatches).toBe(1);
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(1);
        });
        it('should handle mixed scenarios: some match, some skip, some no-match', async () => {
            const perfectMatchTx = 'tx-perfect';
            const perfectMatchDoc = 'doc-perfect';
            const ambiguousTx = 'tx-ambiguous';
            const ambiguousDoc1 = 'doc-ambiguous-1';
            const ambiguousDoc2 = 'doc-ambiguous-2';
            const noMatchTx = 'tx-no-match';
            const mockChargesProvider = {
                getChargesByFilters: vi.fn(() => Promise.resolve([
                    createCharge(perfectMatchTx, ADMIN_BUSINESS_ID),
                    createCharge(perfectMatchDoc, ADMIN_BUSINESS_ID),
                    createCharge(ambiguousTx, ADMIN_BUSINESS_ID),
                    createCharge(ambiguousDoc1, ADMIN_BUSINESS_ID),
                    createCharge(ambiguousDoc2, ADMIN_BUSINESS_ID),
                    createCharge(noMatchTx, ADMIN_BUSINESS_ID),
                ])),
            };
            const mockTransactionsProvider = {
                transactionsByChargeIDLoader: {
                    load: vi.fn((id) => {
                        if (id === perfectMatchTx) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: id,
                                    amount: "100",
                                    currency: 'USD',
                                    event_date: new Date('2024-01-15'),
                                }),
                            ]);
                        }
                        if (id === ambiguousTx) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: id,
                                    amount: "200",
                                    currency: 'EUR',
                                    event_date: new Date('2024-02-15'),
                                }),
                            ]);
                        }
                        if (id === noMatchTx) {
                            return Promise.resolve([
                                createMockTransaction({
                                    charge_id: id,
                                    amount: "500",
                                    currency: 'GBP',
                                    event_date: new Date('2024-03-15'),
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            const mockDocumentsProvider = {
                getDocumentsByChargeIdLoader: {
                    load: vi.fn((id) => {
                        if (id === perfectMatchDoc) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: id,
                                    total_amount: 100,
                                    currency_code: 'USD',
                                    date: new Date('2024-01-15'),
                                    debtor_id: ADMIN_BUSINESS_ID,
                                    creditor_id: 'business-a',
                                }),
                            ]);
                        }
                        if (id === ambiguousDoc1) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: id,
                                    total_amount: 200,
                                    currency_code: 'EUR',
                                    date: new Date('2024-02-15'),
                                    vat_amount: 10, // Different VAT
                                }),
                            ]);
                        }
                        if (id === ambiguousDoc2) {
                            return Promise.resolve([
                                createMockDocument({
                                    charge_id: id,
                                    total_amount: 200,
                                    currency_code: 'EUR',
                                    date: new Date('2024-02-15'),
                                    vat_amount: 20, // Different VAT - but this won't affect matching score
                                }),
                            ]);
                        }
                        return Promise.resolve([]);
                    }),
                },
            };
            mergeChargesExecutor.mockImplementation(() => Promise.resolve());
            const provider = createMockProvider(mockChargesProvider, mockTransactionsProvider, mockDocumentsProvider);
            const result = await provider.autoMatchCharges();
            // Expected results:
            // - perfectMatchTx finds perfectMatchDoc → merge (1)
            // - perfectMatchDoc → already merged, skip processing
            // - ambiguousTx finds 2 docs (ambiguousDoc1, ambiguousDoc2) → skipped due to ambiguity
            // - ambiguousDoc1 finds ambiguousDoc2 (tx was skipped, not merged) → merge (2)
            // - ambiguousDoc2 → already merged, skip processing
            // - noMatchTx → no match, silent
            // Total: 2 successful matches, 1 skipped charge (tx-ambiguous)
            expect(result.totalMatches).toBe(2);
            expect(result.mergedCharges).toHaveLength(2);
            expect(result.skippedCharges).toHaveLength(1);
            expect(result.skippedCharges[0]).toBe(ambiguousTx);
            expect(mergeChargesExecutor).toHaveBeenCalledTimes(2);
        });
        it('should throw error if admin business ID not found in context', async () => {
            const mockAdminContextProvider = {
                getVerifiedAdminContext: vi.fn(() => Promise.reject(new Error('Admin context not found for the authenticated user.'))),
            };
            const provider = new ChargesMatcherProvider(mockAdminContextProvider, { getChargesByFilters: vi.fn() }, { transactionsByChargeIDLoader: { load: vi.fn() } }, { getDocumentsByChargeIdLoader: { load: vi.fn() } }, { injector: {} });
            await expect(provider.autoMatchCharges()).rejects.toThrow(/Admin context not found/);
        });
    });
});
//# sourceMappingURL=auto-match-integration.test.js.map