import { startOfDay } from 'date-fns';
import { EMPTY_UUID } from '../../../shared/constants.js';
import { formatCurrency } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { LedgerProvider } from '../providers/ledger.provider.js';
const comparisonKeys = [
    'credit_entity1',
    'credit_entity2',
    'credit_foreign_amount1',
    'credit_foreign_amount2',
    'credit_local_amount1',
    'credit_local_amount2',
    'currency',
    'debit_entity1',
    'debit_entity2',
    'debit_foreign_amount1',
    'debit_foreign_amount2',
    'debit_local_amount1',
    'debit_local_amount2',
    'description',
    'invoice_date',
    'reference1',
    'value_date',
];
const dateKeys = ['value_date', 'invoice_date'];
export function ledgerRecordsGenerationFullMatchComparison(storageRecords, newRecords) {
    // compare existing records with new records
    const unmatchedStorageRecords = [...storageRecords];
    const unmatchedNewRecords = [];
    const fullMatches = new Map();
    newRecords.map((newRecord, index) => {
        const matchIndex = unmatchedStorageRecords.findIndex(storageRecord => {
            return isExactMatch(storageRecord, newRecord);
        });
        if (matchIndex !== -1) {
            fullMatches.set(index, unmatchedStorageRecords[matchIndex].id);
            unmatchedStorageRecords.splice(matchIndex, 1);
            return;
        }
        unmatchedNewRecords.push(newRecord);
    });
    return {
        unmatchedStorageRecords,
        unmatchedNewRecords,
        fullMatches,
        isFullyMatched: storageRecords.length === newRecords.length && newRecords.length === fullMatches.size,
    };
}
export function ledgerRecordsGenerationPartialMatchComparison(storageRecords, newRecords) {
    const matches = new Map();
    const unmatchedNewRecords = [...newRecords];
    storageRecords.map(storageRecord => {
        let maxScore = 0;
        let bestMatch = undefined;
        unmatchedNewRecords.map(newRecord => {
            const score = getMatchScore(storageRecord, newRecord);
            if (score < 0) {
                return;
            }
            if (score > maxScore) {
                maxScore = score;
                bestMatch = newRecord;
            }
        });
        if (bestMatch) {
            matches.set(storageRecord.id, bestMatch);
            unmatchedNewRecords.splice(unmatchedNewRecords.indexOf(bestMatch), 1);
        }
    });
    const diffs = Array.from(matches.entries()).map(([storageId, newRecord]) => {
        const storageRecord = storageRecords.find(record => record.id === storageId);
        if (!storageRecord) {
            throw new Error('Storage record not found');
        }
        const recordDiffs = {
            ...newRecord,
            id: storageRecord.id,
        };
        return recordDiffs;
    });
    return {
        toUpdate: [
            ...diffs,
            ...unmatchedNewRecords.map(record => ({
                ...record,
                id: EMPTY_UUID,
            })),
        ],
        toRemove: storageRecords.filter(record => !diffs.find(diff => diff.id === record.id)),
    };
}
function isExactMatch(storageRecord, newRecord) {
    return comparisonKeys.reduce((isMatch, key) => {
        if (!isMatch) {
            return false;
        }
        if (dateKeys.includes(key)) {
            const newDate = newRecord[key] ? startOfDay(new Date(newRecord[key])) : undefined;
            return storageRecord[key]?.getTime() === newDate?.getTime();
        }
        if (typeof storageRecord[key] === 'string' && !Number.isNaN(Number(storageRecord[key]))) {
            return Math.abs(Number(storageRecord[key]) - Number(newRecord[key])) < 0.005;
        }
        return storageRecord[key] === newRecord[key];
    }, true);
}
export function convertToStorageInputRecord(record, ownerId) {
    return {
        chargeId: record.chargeId,
        ownerId: record.ownerId ?? ownerId,
        creditEntity1: record.creditAccountID1,
        creditEntity2: record.creditAccountID2,
        creditForeignAmount1: record.creditAmount1,
        creditForeignAmount2: record.creditAmount2,
        creditLocalAmount1: record.localCurrencyCreditAmount1,
        creditLocalAmount2: record.localCurrencyCreditAmount2,
        currency: record.currency,
        debitEntity1: record.debitAccountID1,
        debitEntity2: record.debitAccountID2,
        debitForeignAmount1: record.debitAmount1,
        debitForeignAmount2: record.debitAmount2,
        debitLocalAmount1: record.localCurrencyDebitAmount1,
        debitLocalAmount2: record.localCurrencyDebitAmount2,
        description: record.description,
        invoiceDate: record.invoiceDate,
        reference: record.reference,
        valueDate: record.valueDate,
    };
}
function getMatchScore(storageRecord, newRecord) {
    return comparisonKeys.reduce((cumulativeScore, key) => {
        const factor = storageRecord[key] ? 1 : 0.5;
        let scoreDirection;
        if (dateKeys.includes(key)) {
            scoreDirection =
                storageRecord[key]?.getTime() === newRecord[key]?.getTime() ? 1 : -1;
        }
        else {
            scoreDirection = storageRecord[key] === newRecord[key] ? 1 : -1;
        }
        const addedScore = factor * scoreDirection;
        return cumulativeScore + addedScore;
    }, 0);
}
export function convertLedgerRecordToProto(record, ownerId) {
    return {
        id: record.id,
        creditAccountID1: record.credit_entity1 ?? undefined,
        creditAccountID2: record.credit_entity2 ?? undefined,
        debitAccountID1: record.debit_entity1 ?? undefined,
        debitAccountID2: record.debit_entity2 ?? undefined,
        creditAmount1: record.credit_foreign_amount1
            ? Number(record.credit_foreign_amount1)
            : undefined,
        creditAmount2: record.credit_foreign_amount2
            ? Number(record.credit_foreign_amount2)
            : undefined,
        debitAmount1: record.debit_foreign_amount1 ? Number(record.debit_foreign_amount1) : undefined,
        debitAmount2: record.debit_foreign_amount2 ? Number(record.debit_foreign_amount2) : undefined,
        localCurrencyCreditAmount1: Number(record.credit_local_amount1),
        localCurrencyCreditAmount2: record.credit_local_amount2
            ? Number(record.credit_local_amount2)
            : undefined,
        localCurrencyDebitAmount1: Number(record.debit_local_amount1),
        localCurrencyDebitAmount2: record.debit_local_amount2
            ? Number(record.debit_local_amount2)
            : undefined,
        description: record.description ?? undefined,
        invoiceDate: record.invoice_date,
        reference: record.reference1 ?? undefined,
        valueDate: record.value_date,
        currency: formatCurrency(record.currency),
        isCreditorCounterparty: false, // redundant value
        ownerId: record.owner_id ?? ownerId,
        currencyRate: undefined,
        chargeId: record.charge_id,
    };
}
export function convertLedgerRecordToInput(record, ownerId) {
    return {
        chargeId: record.charge_id ?? undefined,
        ownerId: record.owner_id ?? ownerId,
        creditEntity1: record.credit_entity1 ?? undefined,
        creditEntity2: record.credit_entity2 ?? undefined,
        debitEntity1: record.debit_entity1 ?? undefined,
        debitEntity2: record.debit_entity2 ?? undefined,
        creditForeignAmount1: record.credit_foreign_amount1
            ? Number(record.credit_foreign_amount1)
            : undefined,
        creditForeignAmount2: record.credit_foreign_amount2
            ? Number(record.credit_foreign_amount2)
            : undefined,
        debitForeignAmount1: record.debit_foreign_amount1
            ? Number(record.debit_foreign_amount1)
            : undefined,
        debitForeignAmount2: record.debit_foreign_amount2
            ? Number(record.debit_foreign_amount2)
            : undefined,
        creditLocalAmount1: Number(record.credit_local_amount1),
        creditLocalAmount2: record.credit_local_amount2
            ? Number(record.credit_local_amount2)
            : undefined,
        debitLocalAmount1: Number(record.debit_local_amount1),
        debitLocalAmount2: record.debit_local_amount2 ? Number(record.debit_local_amount2) : undefined,
        description: record.description ?? undefined,
        invoiceDate: record.invoice_date,
        reference: record.reference1 ?? undefined,
        valueDate: record.value_date,
        currency: formatCurrency(record.currency),
        ledgerId: record.id,
    };
}
export async function storeInitialGeneratedRecords(chargeId, records, injector) {
    const ledgerRecords = await injector
        .get(LedgerProvider)
        .getLedgerRecordsByChargesIdLoader.load(chargeId);
    if (ledgerRecords.length === 0) {
        const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext();
        const ledgerRecords = records.map(record => convertToStorageInputRecord(record, ownerId));
        if (ledgerRecords.length) {
            return injector.get(LedgerProvider).insertLedgerRecords({ ledgerRecords });
        }
    }
    return void 0;
}
//# sourceMappingURL=ledgrer-storage.helper.js.map