import { GraphQLError } from 'graphql';
import { Currency } from '../../../shared/enums.js';
import { dateToTimelessDateString, formatFinancialAmount } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { ChargesProvider } from '../../charges/providers/charges.provider.js';
import { LedgerProvider } from '../../ledger/providers/ledger.provider.js';
import { handleBusinessTransaction } from '../helpers/business-transactions.helper.js';
import { FinancialEntitiesProvider } from '../providers/financial-entities.provider.js';
import { businessTransactionsSumFromLedgerRecords } from './business-transactions-sum-from-ledger-records.resolver.js';
export const businessTransactionsResolvers = {
    Query: {
        businessTransactionsSumFromLedgerRecords,
        businessTransactionsFromLedgerRecords: async (_, { filters }, context, _info) => {
            const { injector } = context;
            const { ownerIds, businessIDs, fromDate, toDate, type } = filters || {};
            const shouldFetchAllFinancialEntities = !businessIDs?.length && !!type;
            const financialEntities = await (shouldFetchAllFinancialEntities
                ? injector.get(FinancialEntitiesProvider).getAllFinancialEntities()
                : injector
                    .get(FinancialEntitiesProvider)
                    .getFinancialEntityByIdLoader.loadMany(businessIDs ?? []));
            const isFilteredByFinancialEntities = !!filters?.businessIDs?.length;
            const financialEntitiesIDs = financialEntities
                ?.filter(fe => {
                if (!fe || !('id' in fe)) {
                    return false;
                }
                if (type) {
                    return type.toLocaleLowerCase() === fe.type;
                }
                return true;
            })
                .map(business => business.id);
            try {
                const charges = await injector.get(ChargesProvider).getChargesByFilters({
                    ownerIds: ownerIds ?? undefined,
                    businessIds: businessIDs,
                    fromAnyDate: fromDate,
                    toAnyDate: toDate,
                });
                const ledgerRecordSets = await Promise.all(charges.map(charge => injector.get(LedgerProvider).getLedgerRecordsByChargesIdLoader.load(charge.id)));
                const ledgerRecords = ledgerRecordSets.flat();
                const rawTransactions = [];
                for (const record of ledgerRecords) {
                    // re-filter ledger records by date (to prevent charge's out-of-range dates from affecting the sum)
                    if (!!fromDate && dateToTimelessDateString(record.invoice_date) < fromDate) {
                        continue;
                    }
                    if (!!toDate && dateToTimelessDateString(record.invoice_date) > toDate) {
                        continue;
                    }
                    if (record.credit_entity1 &&
                        (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.credit_entity1))) {
                        const transaction = handleBusinessTransaction(record, record.credit_entity1, record.debit_entity1, true, record.credit_local_amount1, record.credit_foreign_amount1);
                        rawTransactions.push(transaction);
                    }
                    if (record.credit_entity2 &&
                        (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.credit_entity2))) {
                        const transaction = handleBusinessTransaction(record, record.credit_entity2, record.debit_entity2 ?? record.debit_entity2, true, record.credit_local_amount2, record.credit_foreign_amount2);
                        rawTransactions.push(transaction);
                    }
                    if (record.debit_entity1 &&
                        (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.debit_entity1))) {
                        const transaction = handleBusinessTransaction(record, record.debit_entity1, record.credit_entity1, false, record.debit_local_amount1, record.debit_foreign_amount1);
                        rawTransactions.push(transaction);
                    }
                    if (record.debit_entity2 &&
                        (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.debit_entity2))) {
                        const transaction = handleBusinessTransaction(record, record.debit_entity2, record.credit_entity2 ?? record.credit_entity1, false, record.debit_local_amount2, record.debit_foreign_amount2);
                        rawTransactions.push(transaction);
                    }
                }
                return {
                    businessTransactions: rawTransactions.sort((a, b) => {
                        const chargeA = charges.find(charge => charge.id === a.chargeId);
                        const chargeB = charges.find(charge => charge.id === b.chargeId);
                        const dateA = Math.min(...[
                            chargeA?.documents_min_date?.getTime(),
                            chargeA?.transactions_min_event_date?.getTime(),
                        ].filter(Boolean));
                        const dateB = Math.min(...[
                            chargeB?.documents_min_date?.getTime(),
                            chargeB?.transactions_min_event_date?.getTime(),
                        ].filter(Boolean));
                        if (dateA < dateB)
                            return -1;
                        if (dateA > dateB)
                            return 1;
                        if (a.chargeId < b.chargeId)
                            return -1;
                        if (a.chargeId > b.chargeId)
                            return 1;
                        if (a.date.getTime() < b.date.getTime())
                            return -1;
                        if (a.date.getTime() > b.date.getTime())
                            return 1;
                        return 0;
                    }),
                };
            }
            catch (e) {
                console.error(e);
                return {
                    __typename: 'CommonError',
                    message: 'Error fetching business transactions from ledger records',
                };
            }
        },
    },
    BusinessTransactionsSumFromLedgerRecordsResult: {
        __resolveType: (obj, _context, _info) => {
            if ('__typename' in obj && obj.__typename === 'CommonError')
                return 'CommonError';
            return 'BusinessTransactionsSumFromLedgerRecordsSuccessfulResult';
        },
    },
    BusinessTransactionSum: {
        business: (rawSum, _, { injector }) => injector
            .get(FinancialEntitiesProvider)
            .getFinancialEntityByIdLoader.load(rawSum.businessId)
            .then(res => {
            if (!res) {
                throw new GraphQLError(`Business with id ${rawSum.businessId} not found`);
            }
            return res;
        }),
        credit: async (rawSum, _, { injector }) => {
            const { defaultLocalCurrency } = await injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            return formatFinancialAmount(rawSum[defaultLocalCurrency].credit, defaultLocalCurrency);
        },
        debit: async (rawSum, _, { injector }) => {
            const { defaultLocalCurrency } = await injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            return formatFinancialAmount(rawSum[defaultLocalCurrency].debit, defaultLocalCurrency);
        },
        total: async (rawSum, _, { injector }) => {
            const { defaultLocalCurrency } = await injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            return formatFinancialAmount(rawSum[defaultLocalCurrency].total, defaultLocalCurrency);
        },
        foreignCurrenciesSum: async (rawSum, _, { injector }) => {
            const { defaultLocalCurrency } = await injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            const currencies = [];
            for (const key in rawSum) {
                if (!Object.values(Currency).includes(key))
                    continue;
                const currency = key;
                if (currency === defaultLocalCurrency)
                    continue;
                const currencySum = rawSum[currency];
                if (currencySum.credit || currencySum.debit) {
                    currencies.push({
                        credit: formatFinancialAmount(currencySum.credit, currency),
                        debit: formatFinancialAmount(currencySum.debit, currency),
                        total: formatFinancialAmount(currencySum.total, currency),
                        currency,
                    });
                }
            }
            return currencies;
        },
    },
    BusinessTransactionsFromLedgerRecordsResult: {
        __resolveType: parent => 'businessTransactions' in parent
            ? 'BusinessTransactionsFromLedgerRecordsSuccessfulResult'
            : 'CommonError',
    },
    BusinessTransaction: {
        amount: async (parent, _, { injector }) => {
            const { defaultLocalCurrency } = await injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            return formatFinancialAmount(Number.isNaN(parent.foreignAmount)
                ? parent.amount
                : Number(parent.amount) * (parent.isCredit ? 1 : -1), defaultLocalCurrency);
        },
        business: (parent, _, { injector }) => injector
            .get(FinancialEntitiesProvider)
            .getFinancialEntityByIdLoader.load(parent.businessId)
            .then(res => {
            if (!res) {
                throw new GraphQLError(`Financial entity with id ${parent.businessId} not found`);
            }
            return res;
        }),
        foreignAmount: parent => formatFinancialAmount(Number.isNaN(parent.foreignAmount)
            ? parent.foreignAmount
            : Number(parent.foreignAmount) * (parent.isCredit ? 1 : -1), parent.currency),
        invoiceDate: parent => dateToTimelessDateString(parent.date),
        reference: parent => parent.reference ?? null,
        details: parent => parent.details ?? null,
        counterAccount: (parent, _, { injector }) => parent.counterAccountId
            ? injector
                .get(FinancialEntitiesProvider)
                .getFinancialEntityByIdLoader.load(parent.counterAccountId)
                .then(res => {
                if (!res) {
                    throw new GraphQLError(`Financial entity with id ${parent.counterAccountId} not found`);
                }
                return res;
            })
            : null,
        chargeId: parent => parent.chargeId,
    },
};
//# sourceMappingURL=business-transactions.resolver.js.map