import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { getChargeBusinesses, getChargeDocumentsMeta, } from '../../charges/helpers/common.helper.js';
import { ChargeSpreadProvider } from '../../charges/providers/charge-spread.provider.js';
import { FinancialEntitiesProvider } from '../../financial-entities/providers/financial-entities.provider.js';
import { LedgerError } from './utils.helper.js';
function divideAmount(yearsCount, amount) {
    if (yearsCount === 1) {
        return amount;
    }
    return amount ? amount / yearsCount : undefined;
}
function divideEntryAmounts(entry, yearsCount, predefinedAmount = 0, defaultLocalCurrency) {
    const originAmount = entry.currency === defaultLocalCurrency ? entry.localCurrencyDebitAmount1 : entry.debitAmount1;
    const ratio = 1 - Math.abs(predefinedAmount / originAmount);
    return {
        creditAmount1: divideAmount(yearsCount, partialAmountCalculator(entry.creditAmount1, ratio)),
        debitAmount1: divideAmount(yearsCount, partialAmountCalculator(entry.debitAmount1, ratio)),
        localCurrencyCreditAmount1: divideAmount(yearsCount, partialAmountCalculator(entry.localCurrencyCreditAmount1, ratio)),
        localCurrencyDebitAmount1: divideAmount(yearsCount, partialAmountCalculator(entry.localCurrencyDebitAmount1, ratio)),
    };
}
function partialAmountCalculator(amount, ratio = 0) {
    if (amount === undefined) {
        return undefined;
    }
    return amount * ratio;
}
function getPartialEntryAmounts(entry, stringAmount, defaultLocalCurrency) {
    if (stringAmount === null) {
        return null;
    }
    const partialAmount = Number(stringAmount);
    if (Number.isNaN(partialAmount)) {
        return null;
    }
    const mainAmount = getEntryMainAmount(entry, defaultLocalCurrency);
    if ((entry.debitAmount1 || entry.creditAmount1) && partialAmount > mainAmount) {
        throw new LedgerError('Partial amount exceeds ledger record amount');
    }
    const ratio = partialAmount / mainAmount;
    return {
        creditAmount1: partialAmountCalculator(entry.creditAmount1, ratio),
        creditAmount2: partialAmountCalculator(entry.creditAmount2, ratio),
        debitAmount1: partialAmountCalculator(entry.debitAmount1, ratio),
        debitAmount2: partialAmountCalculator(entry.debitAmount2, ratio),
        localCurrencyCreditAmount1: partialAmountCalculator(entry.localCurrencyCreditAmount1, ratio),
        localCurrencyCreditAmount2: partialAmountCalculator(entry.localCurrencyCreditAmount2, ratio),
        localCurrencyDebitAmount1: partialAmountCalculator(entry.localCurrencyDebitAmount1, ratio),
        localCurrencyDebitAmount2: partialAmountCalculator(entry.localCurrencyDebitAmount2, ratio),
    };
}
export async function handleCrossYearLedgerEntries(charge, injector, accountingLedgerEntries) {
    const { defaultLocalCurrency, authorities, crossYear: { expensesToPayTaxCategoryId, expensesInAdvanceTaxCategoryId, incomeInAdvanceTaxCategoryId, incomeToCollectTaxCategoryId, }, } = await injector.get(AdminContextProvider).getVerifiedAdminContext();
    if (accountingLedgerEntries.length === 0) {
        return null;
    }
    if (!expensesToPayTaxCategoryId ||
        !expensesInAdvanceTaxCategoryId ||
        !incomeInAdvanceTaxCategoryId ||
        !incomeToCollectTaxCategoryId) {
        throw new LedgerError('Some cross-year tax categories are not set');
    }
    const spreadRecords = await injector
        .get(ChargeSpreadProvider)
        .getChargeSpreadByChargeIdLoader.load(charge.id);
    if (!spreadRecords?.length) {
        return null;
    }
    if (accountingLedgerEntries.length > 1 && spreadRecords.length > 1) {
        throw new LedgerError('Unable to split multiple documents into multiple years');
    }
    let yearsWithoutSpecifiedAmountCount = 0;
    let predefinedAmount = 0;
    spreadRecords.map(record => {
        if (record.amount === null) {
            yearsWithoutSpecifiedAmountCount += 1;
        }
        else {
            predefinedAmount += Number(record.amount);
        }
    });
    const entriesAmount = accountingLedgerEntries
        .map(entry => {
        const { adjustedEntry } = splitVatPayments(entry, authorities);
        return adjustedEntry;
    })
        .reduce((acc, entry) => {
        const mainAmount = getEntryMainAmount(entry, defaultLocalCurrency);
        return acc + mainAmount;
    }, 0);
    const [{ mainBusinessId }, { documentsMinDate }] = await Promise.all([
        getChargeBusinesses(charge.id, injector),
        getChargeDocumentsMeta(charge.id, injector),
    ]);
    // handle current year auto-added spread record if amounts do not match
    if (yearsWithoutSpecifiedAmountCount === 0 &&
        Math.abs(predefinedAmount - entriesAmount) > 0.005) {
        if (documentsMinDate &&
            !spreadRecords.some(r => r.year_of_relevance.getFullYear() === documentsMinDate.getFullYear())) {
            spreadRecords.push({
                owner_id: charge.owner_id,
                charge_id: charge.id,
                year_of_relevance: documentsMinDate,
                amount: null,
            });
            yearsWithoutSpecifiedAmountCount += 1;
        }
        else {
            throw new Error('Spread amounts do not match ledger entries amounts');
        }
    }
    let description = undefined;
    if (mainBusinessId) {
        try {
            const mainBusiness = await injector
                .get(FinancialEntitiesProvider)
                .getFinancialEntityByIdLoader.load(mainBusinessId);
            if (mainBusiness) {
                description = `Main counterparty: ${mainBusiness.name}`;
            }
        }
        catch (error) {
            console.error('Failed to load financial entity:', error);
        }
    }
    // calculate cross year entries
    const crossYearEntries = [];
    if (accountingLedgerEntries.length === 1) {
        const entry = accountingLedgerEntries[0];
        const { adjustedEntry, vatEntries } = splitVatPayments(entry, authorities);
        crossYearEntries.push(...vatEntries);
        const defaultAmounts = divideEntryAmounts(adjustedEntry, yearsWithoutSpecifiedAmountCount, predefinedAmount, defaultLocalCurrency);
        for (const spreadRecord of spreadRecords) {
            const amounts = getPartialEntryAmounts(adjustedEntry, spreadRecord.amount, defaultLocalCurrency) ??
                defaultAmounts;
            const yearDate = spreadRecord.year_of_relevance;
            if (adjustedEntry.invoiceDate.getFullYear() === yearDate.getFullYear() &&
                adjustedEntry.valueDate.getFullYear() === yearDate.getFullYear()) {
                crossYearEntries.push({ ...adjustedEntry, ...amounts });
                continue;
            }
            const isYearOfRelevancePrior = adjustedEntry.invoiceDate.getFullYear() > yearDate.getFullYear();
            const mediateTaxCategory = !!adjustedEntry.isCreditorCounterparty === !!adjustedEntry.isCreditInvoice
                ? isYearOfRelevancePrior
                    ? incomeToCollectTaxCategoryId
                    : incomeInAdvanceTaxCategoryId
                : isYearOfRelevancePrior
                    ? expensesToPayTaxCategoryId
                    : expensesInAdvanceTaxCategoryId;
            const yearOfRelevanceDates = isYearOfRelevancePrior
                ? { invoiceDate: new Date(yearDate.getFullYear(), 11, 31) }
                : { invoiceDate: new Date(yearDate.getFullYear(), 0, 1) };
            crossYearEntries.push({
                // first chronological entry
                ...adjustedEntry,
                ...amounts,
                ...(adjustedEntry.isCreditorCounterparty
                    ? { creditAccountID1: mediateTaxCategory }
                    : { debitAccountID1: mediateTaxCategory }),
                ...yearOfRelevanceDates,
                ...(isYearOfRelevancePrior ? { vat: undefined, description } : {}),
            }, {
                // second chronological entry
                ...adjustedEntry,
                ...amounts,
                ...(adjustedEntry.isCreditorCounterparty
                    ? { debitAccountID1: mediateTaxCategory }
                    : { creditAccountID1: mediateTaxCategory }),
                ...(isYearOfRelevancePrior ? {} : { vat: undefined, description }),
            });
        }
    }
    else if (spreadRecords.length === 1) {
        const spreadRecord = spreadRecords[0];
        const yearDate = spreadRecord.year_of_relevance;
        validateEntriesAmountsMatchesSpread(spreadRecord.amount, entriesAmount);
        for (const entry of accountingLedgerEntries) {
            const { adjustedEntry, vatEntries } = splitVatPayments(entry, authorities);
            crossYearEntries.push(...vatEntries);
            if (adjustedEntry.invoiceDate.getFullYear() === yearDate.getFullYear() &&
                adjustedEntry.valueDate.getFullYear() === yearDate.getFullYear()) {
                crossYearEntries.push(adjustedEntry);
                continue;
            }
            const isYearOfRelevancePrior = adjustedEntry.invoiceDate.getFullYear() > yearDate.getFullYear();
            const mediateTaxCategory = !!adjustedEntry.isCreditorCounterparty === !!adjustedEntry.isCreditInvoice
                ? isYearOfRelevancePrior
                    ? incomeToCollectTaxCategoryId
                    : incomeInAdvanceTaxCategoryId
                : isYearOfRelevancePrior
                    ? expensesToPayTaxCategoryId
                    : expensesInAdvanceTaxCategoryId;
            const yearOfRelevanceDates = isYearOfRelevancePrior
                ? { invoiceDate: new Date(yearDate.getFullYear(), 11, 31) }
                : { invoiceDate: new Date(yearDate.getFullYear(), 0, 1) };
            crossYearEntries.push({
                // first chronological entry
                ...adjustedEntry,
                ...(adjustedEntry.isCreditorCounterparty
                    ? { creditAccountID1: mediateTaxCategory }
                    : { debitAccountID1: mediateTaxCategory }),
                ...yearOfRelevanceDates,
                ...(isYearOfRelevancePrior ? { vat: undefined, description } : {}),
            }, {
                // second chronological entry
                ...adjustedEntry,
                ...(adjustedEntry.isCreditorCounterparty
                    ? { debitAccountID1: mediateTaxCategory }
                    : { creditAccountID1: mediateTaxCategory }),
                ...(isYearOfRelevancePrior ? {} : { vat: undefined, description }),
            });
        }
    }
    return crossYearEntries;
}
function splitVatPayments(entry, authorities) {
    const { inputVatTaxCategoryId, outputVatTaxCategoryId, propertyOutputVatTaxCategoryId } = authorities;
    const vatTaxIds = [inputVatTaxCategoryId, outputVatTaxCategoryId, propertyOutputVatTaxCategoryId];
    const vatEntries = [];
    const adjustedEntry = { ...entry };
    if (vatTaxIds.includes(entry.creditAccountID2 ?? '')) {
        vatEntries.push({
            ...entry,
            creditAccountID1: entry.creditAccountID2,
            creditAccountID2: undefined,
            creditAmount1: entry.creditAmount2,
            creditAmount2: undefined,
            localCurrencyCreditAmount1: entry.localCurrencyCreditAmount2,
            localCurrencyCreditAmount2: undefined,
            localCurrencyDebitAmount1: entry.localCurrencyCreditAmount2,
            localCurrencyDebitAmount2: undefined,
        });
        // remove VAT from main entry
        adjustedEntry.debitAmount1 &&= adjustedEntry.debitAmount1 - (entry.creditAmount2 ?? 0);
        adjustedEntry.localCurrencyDebitAmount1 &&=
            adjustedEntry.localCurrencyDebitAmount1 - entry.localCurrencyCreditAmount2;
        adjustedEntry.creditAccountID2 = undefined;
        adjustedEntry.creditAmount2 = undefined;
        adjustedEntry.localCurrencyCreditAmount2 = undefined;
    }
    else if (vatTaxIds.includes(entry.debitAccountID2 ?? '')) {
        vatEntries.push({
            ...entry,
            debitAccountID1: entry.debitAccountID2,
            debitAmount1: entry.debitAmount2,
            creditAmount1: entry.debitAmount2,
            localCurrencyDebitAmount1: entry.localCurrencyDebitAmount2,
            localCurrencyCreditAmount1: entry.localCurrencyDebitAmount2,
            debitAccountID2: undefined,
            debitAmount2: undefined,
            localCurrencyDebitAmount2: undefined,
            creditAccountID2: undefined,
            creditAmount2: undefined,
            localCurrencyCreditAmount2: undefined,
        });
        // remove VAT from main entry
        adjustedEntry.creditAmount1 &&= adjustedEntry.creditAmount1 - (entry.debitAmount2 ?? 0);
        adjustedEntry.localCurrencyCreditAmount1 &&=
            adjustedEntry.localCurrencyCreditAmount1 - entry.localCurrencyDebitAmount2;
        adjustedEntry.debitAccountID2 = undefined;
        adjustedEntry.debitAmount2 = undefined;
        adjustedEntry.localCurrencyDebitAmount2 = undefined;
    }
    if (adjustedEntry.creditAmount1 !== adjustedEntry.debitAmount1) {
        throw new LedgerError('Credit and debit amounts are not equal');
    }
    return { adjustedEntry, vatEntries };
}
function getEntryMainAmount(entry, defaultLocalCurrency) {
    const mainAmount = Math.abs(Math.max(...(entry.currency === defaultLocalCurrency
        ? [entry.localCurrencyDebitAmount1 ?? 0, entry.localCurrencyCreditAmount1 ?? 0]
        : [entry.debitAmount1 ?? 0, entry.creditAmount1 ?? 0])));
    return mainAmount;
}
function validateEntriesAmountsMatchesSpread(spreadStringifiedAmount, entriesAmount) {
    if (spreadStringifiedAmount === null) {
        return;
    }
    const spreadAmount = Number(spreadStringifiedAmount);
    if (Number.isNaN(spreadAmount)) {
        return;
    }
    if (spreadAmount !== entriesAmount) {
        throw new LedgerError('Spread amount does not match ledger entries amounts');
    }
}
//# sourceMappingURL=cross-year-ledger.helper.js.map