import { GraphQLError } from 'graphql';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { TransactionsProvider } from '../../transactions/providers/transactions.provider.js';
import { coreExpenseUpdate, createTravelAndSubsistenceExpense, generateChargeForEmployeePayment, updateExistingTripExpense, } from '../helpers/business-trips-expenses.helper.js';
import { BusinessTripAttendeesProvider } from '../providers/business-trips-attendees.provider.js';
import { BusinessTripEmployeePaymentsProvider } from '../providers/business-trips-employee-payments.provider.js';
import { BusinessTripAccommodationsExpensesProvider } from '../providers/business-trips-expenses-accommodations.provider.js';
import { BusinessTripCarRentalExpensesProvider } from '../providers/business-trips-expenses-car-rental.provider.js';
import { BusinessTripFlightsExpensesProvider } from '../providers/business-trips-expenses-flights.provider.js';
import { BusinessTripOtherExpensesProvider } from '../providers/business-trips-expenses-other.provider.js';
import { BusinessTripExpensesTransactionsMatchProvider } from '../providers/business-trips-expenses-transactions-match.provider.js';
import { BusinessTripTravelAndSubsistenceExpensesProvider } from '../providers/business-trips-expenses-travel-and-subsistence.provider.js';
import { BusinessTripExpensesProvider } from '../providers/business-trips-expenses.provider.js';
import { commonBusinessTripExpenseFields } from './common.js';
import { creditShareholdersBusinessTripTravelAndSubsistence } from './credit-shareholders-business-trip-travel-and-subsistence.resolver.js';
export const businessTripExpensesResolvers = {
    Mutation: {
        categorizeBusinessTripExpense: async (_, { fields: { businessTripId, transactionId, category, amount } }, { injector }) => {
            try {
                if (!category) {
                    throw new GraphQLError(`Category is required`);
                }
                const [businessTripExpense] = await injector
                    .get(BusinessTripExpensesProvider)
                    .insertBusinessTripExpense({
                    businessTripId,
                    category,
                });
                const id = businessTripExpense.id;
                if (!amount) {
                    const transaction = await injector
                        .get(TransactionsProvider)
                        .transactionByIdLoader.load(transactionId);
                    if (transaction?.amount) {
                        amount = Number(transaction.amount);
                    }
                }
                await updateExistingTripExpense(injector, id, transactionId, category, amount);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip expense's category`, e);
                throw new GraphQLError("Error updating charge's business trip");
            }
        },
        categorizeIntoExistingBusinessTripExpense: async (_, { fields: { businessTripExpenseId, transactionId, amount } }, { injector }) => {
            try {
                if (!transactionId) {
                    throw new GraphQLError(`Expense ID is required`);
                }
                const businessTripExpense = await injector
                    .get(BusinessTripExpensesProvider)
                    .getBusinessTripsExpensesByIdLoader.load(businessTripExpenseId);
                if (!businessTripExpense) {
                    throw new GraphQLError(`Business trip expense with id ${businessTripExpenseId} not found`);
                }
                await injector
                    .get(BusinessTripExpensesTransactionsMatchProvider)
                    .insertBusinessTripExpenseMatch({
                    businessTripExpenseId,
                    transactionId,
                    amount,
                });
                return businessTripExpense.id;
            }
            catch (e) {
                console.error(`Error updating business trip expense's category`, e);
                throw new GraphQLError("Error updating charge's business trip");
            }
        },
        updateBusinessTripFlightsExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpenseUpdatePromise = coreExpenseUpdate(injector, fields, 'FLIGHT');
                const { id, path, flightClass, attendeeIds } = fields;
                const hasFlightFieldsToUpdate = path || flightClass || attendeeIds?.length;
                const flightExpenseUpdate = hasFlightFieldsToUpdate
                    ? injector.get(BusinessTripFlightsExpensesProvider).updateBusinessTripFlightsExpense({
                        businessTripExpenseId: id,
                        path: path ? [...path] : undefined,
                        class: flightClass,
                        attendeeIds: fields.attendeeIds,
                    })
                    : Promise.resolve();
                await Promise.all([coreExpenseUpdatePromise, flightExpenseUpdate]);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip flight expense`, e);
                throw new GraphQLError('Error updating business trip flight expense');
            }
        },
        updateBusinessTripAccommodationsExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpenseUpdatePromise = coreExpenseUpdate(injector, fields, 'ACCOMMODATION');
                const { id, country, nightsCount, attendeesStay } = fields;
                const hasAccommodationFieldsToUpdate = country || nightsCount || attendeesStay;
                const accommodationExpenseUpdate = hasAccommodationFieldsToUpdate
                    ? injector
                        .get(BusinessTripAccommodationsExpensesProvider)
                        .updateBusinessTripAccommodationsExpense({
                        businessTripExpenseId: id,
                        country,
                        nightsCount,
                        attendeesStay: fields.attendeesStay,
                    })
                    : Promise.resolve();
                await Promise.all([coreExpenseUpdatePromise, accommodationExpenseUpdate]);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip accommodations expense`, e);
                throw new GraphQLError('Error updating business trip accommodations expense');
            }
        },
        updateBusinessTripOtherExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpenseUpdatePromise = coreExpenseUpdate(injector, fields, 'OTHER');
                const { id, description, deductibleExpense } = fields;
                const hasOtherFieldsToUpdate = description || deductibleExpense != null;
                const otherExpenseUpdate = hasOtherFieldsToUpdate
                    ? injector.get(BusinessTripOtherExpensesProvider).updateBusinessTripOtherExpense({
                        businessTripExpenseId: id,
                        description,
                        deductibleExpense,
                    })
                    : Promise.resolve();
                await Promise.all([coreExpenseUpdatePromise, otherExpenseUpdate]);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip other expense`, e);
                throw new GraphQLError('Error updating business trip other expense');
            }
        },
        updateBusinessTripTravelAndSubsistenceExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpenseUpdatePromise = coreExpenseUpdate(injector, fields, 'TRAVEL_AND_SUBSISTENCE');
                const { id, expenseType } = fields;
                const hasTravelAndSubsistenceFieldsToUpdate = expenseType;
                const travelAndSubsistenceExpenseUpdate = hasTravelAndSubsistenceFieldsToUpdate
                    ? injector
                        .get(BusinessTripTravelAndSubsistenceExpensesProvider)
                        .updateBusinessTripTravelAndSubsistenceExpense({
                        businessTripExpenseId: id,
                        expenseType,
                    })
                    : Promise.resolve();
                await Promise.all([coreExpenseUpdatePromise, travelAndSubsistenceExpenseUpdate]);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip travel&subsistence expense`, e);
                throw new GraphQLError('Error updating business trip travel&subsistence expense');
            }
        },
        updateBusinessTripCarRentalExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpenseUpdatePromise = coreExpenseUpdate(injector, fields, 'CAR_RENTAL');
                const { id, days, isFuelExpense } = fields;
                const hasCarRentalFieldsToUpdate = isFuelExpense != null || days != null;
                const carRentalExpenseUpdate = hasCarRentalFieldsToUpdate
                    ? injector.get(BusinessTripCarRentalExpensesProvider).updateBusinessTripCarRentalExpense({
                        businessTripExpenseId: id,
                        days,
                        isFuelExpense,
                    })
                    : Promise.resolve();
                await Promise.all([coreExpenseUpdatePromise, carRentalExpenseUpdate]);
                return id;
            }
            catch (e) {
                console.error(`Error updating business trip car rental expense`, e);
                throw new GraphQLError('Error updating business trip car rental expense');
            }
        },
        deleteBusinessTripExpense: async (_, { businessTripExpenseId }, { injector }) => {
            try {
                await Promise.all([
                    injector
                        .get(BusinessTripFlightsExpensesProvider)
                        .deleteBusinessTripFlightsExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripAccommodationsExpensesProvider)
                        .deleteBusinessTripAccommodationsExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripOtherExpensesProvider)
                        .deleteBusinessTripOtherExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripTravelAndSubsistenceExpensesProvider)
                        .deleteBusinessTripTravelAndSubsistenceExpense({ businessTripExpenseId }),
                    injector.get(BusinessTripEmployeePaymentsProvider).deleteBusinessTripEmployeePayment({
                        businessTripExpenseId,
                    }),
                    injector
                        .get(BusinessTripExpensesTransactionsMatchProvider)
                        .deleteBusinessTripExpenseMatch({ businessTripExpenseId }),
                ]);
                // core expense must be deleted AFTER all extensions were dropped
                await injector
                    .get(BusinessTripExpensesProvider)
                    .deleteBusinessTripExpense({ businessTripExpenseId });
                return true;
            }
            catch (e) {
                console.error(`Error deleting business trip expense`, e);
                throw new GraphQLError('Error deleting business trip expense');
            }
        },
        uncategorizePartialBusinessTripExpense: async (_, { transactionId, businessTripExpenseId }, { injector }) => {
            try {
                await Promise.all([
                    injector
                        .get(BusinessTripFlightsExpensesProvider)
                        .deleteBusinessTripFlightsExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripAccommodationsExpensesProvider)
                        .deleteBusinessTripAccommodationsExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripOtherExpensesProvider)
                        .deleteBusinessTripOtherExpense({ businessTripExpenseId }),
                    injector
                        .get(BusinessTripTravelAndSubsistenceExpensesProvider)
                        .deleteBusinessTripTravelAndSubsistenceExpense({ businessTripExpenseId }),
                    injector.get(BusinessTripEmployeePaymentsProvider).deleteBusinessTripEmployeePayment({
                        businessTripExpenseId,
                    }),
                    injector
                        .get(BusinessTripExpensesTransactionsMatchProvider)
                        .deleteSpecificBusinessTripExpenseMatch({
                        businessTripExpenseId,
                        transactionId,
                    }),
                ]);
                return true;
            }
            catch (e) {
                console.error(`Error deleting business trip expense part`, e);
                throw new GraphQLError('Error deleting business trip expense part');
            }
        },
        addBusinessTripFlightsExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpensePromise = injector
                    .get(BusinessTripExpensesProvider)
                    .insertBusinessTripExpense({
                    businessTripId: fields.businessTripId,
                    category: 'FLIGHT',
                })
                    .then(res => res[0]);
                const chargeGenerationPromise = generateChargeForEmployeePayment(injector, fields.businessTripId);
                const [coreExpense, chargeId] = await Promise.all([
                    coreExpensePromise,
                    chargeGenerationPromise,
                ]);
                await Promise.all([
                    injector.get(BusinessTripFlightsExpensesProvider).insertBusinessTripFlightsExpense({
                        id: coreExpense.id,
                        path: fields.path ? [...fields.path] : undefined,
                        class: fields.flightClass,
                        attendeeIds: fields.attendeeIds,
                    }),
                    injector.get(BusinessTripEmployeePaymentsProvider).insertBusinessTripEmployeePayment({
                        businessTripExpenseId: coreExpense.id,
                        chargeId,
                        date: fields.date,
                        valueDate: fields.valueDate,
                        amount: fields.amount,
                        currency: fields.currency,
                        employeeBusinessId: fields.employeeBusinessId,
                    }),
                ]);
                return coreExpense.id;
            }
            catch (e) {
                console.error(`Error adding new business trip flight expense`, e);
                throw new GraphQLError('Error adding new business trip flight expense');
            }
        },
        addBusinessTripAccommodationsExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpensePromise = injector
                    .get(BusinessTripExpensesProvider)
                    .insertBusinessTripExpense({
                    businessTripId: fields.businessTripId,
                    category: 'ACCOMMODATION',
                })
                    .then(res => res[0]);
                const chargeGenerationPromise = generateChargeForEmployeePayment(injector, fields.businessTripId);
                const [coreExpense, chargeId] = await Promise.all([
                    coreExpensePromise,
                    chargeGenerationPromise,
                ]);
                await Promise.all([
                    injector
                        .get(BusinessTripAccommodationsExpensesProvider)
                        .insertBusinessTripAccommodationsExpense({
                        id: coreExpense.id,
                        country: fields.country,
                        nightsCount: fields.nightsCount,
                        attendeesStay: fields.attendeesStay,
                    }),
                    injector.get(BusinessTripEmployeePaymentsProvider).insertBusinessTripEmployeePayment({
                        businessTripExpenseId: coreExpense.id,
                        chargeId,
                        date: fields.date,
                        valueDate: fields.valueDate,
                        amount: fields.amount,
                        currency: fields.currency,
                        employeeBusinessId: fields.employeeBusinessId,
                    }),
                ]);
                return coreExpense.id;
            }
            catch (e) {
                console.error(`Error adding new business trip accommodation expense`, e);
                throw new GraphQLError('Error adding new business trip accommodation expense');
            }
        },
        addBusinessTripOtherExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpensePromise = injector
                    .get(BusinessTripExpensesProvider)
                    .insertBusinessTripExpense({
                    businessTripId: fields.businessTripId,
                    category: 'OTHER',
                })
                    .then(res => res[0]);
                const chargeGenerationPromise = generateChargeForEmployeePayment(injector, fields.businessTripId);
                const [coreExpense, chargeId] = await Promise.all([
                    coreExpensePromise,
                    chargeGenerationPromise,
                ]);
                await Promise.all([
                    injector.get(BusinessTripOtherExpensesProvider).insertBusinessTripOtherExpense({
                        id: coreExpense.id,
                        description: fields.description,
                        deductibleExpense: fields.deductibleExpense,
                    }),
                    injector.get(BusinessTripEmployeePaymentsProvider).insertBusinessTripEmployeePayment({
                        businessTripExpenseId: coreExpense.id,
                        chargeId,
                        date: fields.date,
                        valueDate: fields.valueDate,
                        amount: fields.amount,
                        currency: fields.currency,
                        employeeBusinessId: fields.employeeBusinessId,
                    }),
                ]);
                return coreExpense.id;
            }
            catch (e) {
                console.error(`Error adding new business trip other expense`, e);
                throw new GraphQLError('Error adding new business trip other expense');
            }
        },
        addBusinessTripTravelAndSubsistenceExpense: async (_, { fields }, context) => {
            const adminContext = await context.injector
                .get(AdminContextProvider)
                .getVerifiedAdminContext();
            if (adminContext.ledgerLock) {
                if (fields.date && fields.date < adminContext.ledgerLock) {
                    throw new GraphQLError('Cannot add expense before ledger lock date');
                }
                if (fields.valueDate && fields.valueDate < adminContext.ledgerLock) {
                    throw new GraphQLError('Cannot add expense before ledger lock date');
                }
            }
            return createTravelAndSubsistenceExpense(context, fields);
        },
        addBusinessTripCarRentalExpense: async (_, { fields }, { injector }) => {
            try {
                const coreExpensePromise = injector
                    .get(BusinessTripExpensesProvider)
                    .insertBusinessTripExpense({
                    businessTripId: fields.businessTripId,
                    category: 'CAR_RENTAL',
                })
                    .then(res => res[0]);
                const chargeGenerationPromise = generateChargeForEmployeePayment(injector, fields.businessTripId);
                const [coreExpense, chargeId] = await Promise.all([
                    coreExpensePromise,
                    chargeGenerationPromise,
                ]);
                await Promise.all([
                    injector.get(BusinessTripCarRentalExpensesProvider).insertBusinessTripCarRentalExpense({
                        id: coreExpense.id,
                        days: fields.days ?? 0,
                        isFuelExpense: fields.isFuelExpense ?? false,
                    }),
                    injector.get(BusinessTripEmployeePaymentsProvider).insertBusinessTripEmployeePayment({
                        businessTripExpenseId: coreExpense.id,
                        chargeId,
                        date: fields.date,
                        valueDate: fields.valueDate,
                        amount: fields.amount,
                        currency: fields.currency,
                        employeeBusinessId: fields.employeeBusinessId,
                    }),
                ]);
                return coreExpense.id;
            }
            catch (e) {
                console.error(`Error adding new business trip car rental expense`, e);
                throw new GraphQLError('Error adding new business trip car rental expense');
            }
        },
        creditShareholdersBusinessTripTravelAndSubsistence,
    },
    BusinessTripAccommodationExpense: {
        __isTypeOf: DbExpense => DbExpense.category === 'ACCOMMODATION',
        ...commonBusinessTripExpenseFields,
        country: dbExpense => dbExpense.country,
        nightsCount: dbExpense => dbExpense.nights_count,
        attendeesStay: async (dbExpense, _, { injector }) => {
            if (dbExpense.attendees_stay.length === 0 || !dbExpense.business_trip_id) {
                return [];
            }
            const attendeesStay = dbExpense.attendees_stay.filter(Boolean);
            const attendeesStayIds = attendeesStay.map(({ attendeeId }) => attendeeId);
            const attendees = await injector
                .get(BusinessTripAttendeesProvider)
                .getBusinessTripsAttendeesByBusinessTripIdLoader.load(dbExpense.business_trip_id);
            return attendees
                .filter(attendee => attendeesStayIds.includes(attendee.id))
                .map(attendee => ({
                id: attendee.id,
                attendee,
                nightsCount: attendeesStay.find(({ attendeeId }) => attendeeId === attendee.id)?.nightsCount ?? 0,
            }));
        },
    },
    BusinessTripFlightExpense: {
        __isTypeOf: DbExpense => DbExpense.category === 'FLIGHT',
        ...commonBusinessTripExpenseFields,
        path: dbExpense => dbExpense.path,
        class: dbExpense => dbExpense.class,
        attendees: async (dbExpense, _, { injector }) => {
            if (dbExpense.attendees.length === 0 || !dbExpense.business_trip_id) {
                return [];
            }
            const attendees = await injector
                .get(BusinessTripAttendeesProvider)
                .getBusinessTripsAttendeesByBusinessTripIdLoader.load(dbExpense.business_trip_id);
            return attendees.filter(attendee => dbExpense.attendees.includes(attendee.id));
        },
    },
    BusinessTripTravelAndSubsistenceExpense: {
        __isTypeOf: DbExpense => DbExpense.category === 'TRAVEL_AND_SUBSISTENCE',
        ...commonBusinessTripExpenseFields,
        expenseType: dbExpense => dbExpense.expense_type,
    },
    BusinessTripCarRentalExpense: {
        __isTypeOf: DbExpense => DbExpense.category === 'CAR_RENTAL',
        ...commonBusinessTripExpenseFields,
        days: dbExpense => dbExpense.days,
        isFuelExpense: dbExpense => dbExpense.is_fuel_expense,
    },
    BusinessTripOtherExpense: {
        __isTypeOf: DbExpense => DbExpense.category === 'OTHER',
        ...commonBusinessTripExpenseFields,
        description: dbExpense => dbExpense.description,
    },
};
//# sourceMappingURL=business-trips-expenses.resolver.js.map