import { GraphQLError } from 'graphql';
import { dateToTimelessDateString, formatFinancialAmount } from '../../../shared/helpers/index.js';
import { ChargesProvider } from '../../charges/providers/charges.provider.js';
import { getTransactionMatchedAmount } from '../helpers/business-trips-expenses.helper.js';
import { BusinessTripAttendeesProvider } from '../providers/business-trips-attendees.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 { BusinessTripTravelAndSubsistenceExpensesProvider } from '../providers/business-trips-expenses-travel-and-subsistence.provider.js';
import { BusinessTripExpensesProvider } from '../providers/business-trips-expenses.provider.js';
import { BusinessTripsProvider } from '../providers/business-trips.provider.js';
import { businessTripSummary } from './business-trip-summary.resolver.js';
export const businessTripsResolvers = {
    Query: {
        allBusinessTrips: async (_, __, { injector }) => {
            try {
                return injector.get(BusinessTripsProvider).getAllBusinessTrips();
            }
            catch (e) {
                console.error('Error fetching business trips', e);
                throw new GraphQLError(e?.message ?? 'Error fetching business trips');
            }
        },
        businessTrip: async (_, { id }, { injector }) => {
            try {
                return injector
                    .get(BusinessTripsProvider)
                    .getBusinessTripsByIdLoader.load(id)
                    .then(businessTrip => businessTrip ?? null);
            }
            catch (e) {
                console.error('Error fetching business trips', e);
                throw new GraphQLError(e?.message ?? 'Error fetching business trips');
            }
        },
    },
    Mutation: {
        updateChargeBusinessTrip: async (_, { chargeId, businessTripId = null }, { injector }) => {
            try {
                const [updatedChargeId] = await injector
                    .get(BusinessTripsProvider)
                    .updateChargeBusinessTrip(chargeId, businessTripId);
                if (updatedChargeId) {
                    return injector
                        .get(ChargesProvider)
                        .getChargeByIdLoader.load(updatedChargeId.charge_id)
                        .then(charge => {
                        if (charge) {
                            return charge;
                        }
                        throw new Error(`Updated charge with id ${updatedChargeId} not found`);
                    });
                }
                throw new Error('Error updating charge business trip');
            }
            catch (e) {
                console.error(`Error updating charge's business trip`, e);
                throw new GraphQLError(e?.message ?? `Error updating charge's business trip`);
            }
        },
        insertBusinessTrip: async (_, { fields }, { injector }) => {
            // fields validation
            if (!fields.name) {
                throw new GraphQLError(`Business trip name is required`);
            }
            try {
                return injector
                    .get(BusinessTripsProvider)
                    .insertBusinessTrip(fields)
                    .then(result => result[0]?.id);
            }
            catch (e) {
                console.error(`Error inserting business trip`, e);
                throw new GraphQLError(e?.message ?? `Error inserting business trip`);
            }
        },
        updateBusinessTrip: async (_, { fields }, { injector }) => {
            try {
                return injector
                    .get(BusinessTripsProvider)
                    .insertBusinessTrip(fields)
                    .then(result => result[0]?.id);
            }
            catch (e) {
                console.error(`Error updating business trip`, e);
                throw new GraphQLError(e?.message ?? `Error inserting business trip`);
            }
        },
    },
    BusinessTrip: {
        id: dbBusinessTrip => dbBusinessTrip.id,
        name: dbBusinessTrip => dbBusinessTrip.name,
        dates: async (dbBusinessTrip, _, { injector }) => {
            const attendees = await injector
                .get(BusinessTripAttendeesProvider)
                .getBusinessTripsAttendeesByBusinessTripIdLoader.load(dbBusinessTrip.id);
            if (!attendees?.length) {
                return null;
            }
            let fromDate;
            let toDate;
            attendees.map(attendee => {
                const { arrival, departure } = attendee;
                if (arrival && (!fromDate || arrival < fromDate)) {
                    fromDate = arrival;
                }
                if (departure && (!toDate || departure > toDate)) {
                    toDate = departure;
                }
            });
            if (!fromDate || !toDate) {
                return null;
            }
            return {
                start: dateToTimelessDateString(fromDate),
                end: dateToTimelessDateString(toDate),
            };
        },
        purpose: dbBusinessTrip => dbBusinessTrip.trip_purpose,
        destination: dbBusinessTrip => dbBusinessTrip.destination ?? null,
        attendees: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripAttendeesProvider)
                .getBusinessTripsAttendeesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        flightExpenses: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripFlightsExpensesProvider)
                .getBusinessTripsFlightsExpensesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        accommodationExpenses: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripAccommodationsExpensesProvider)
                .getBusinessTripsAccommodationsExpensesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        travelAndSubsistenceExpenses: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripTravelAndSubsistenceExpensesProvider)
                .getBusinessTripsTravelAndSubsistenceExpensesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        otherExpenses: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripOtherExpensesProvider)
                .getBusinessTripsOtherExpensesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        carRentalExpenses: async (dbBusinessTrip, _, { injector }) => {
            return injector
                .get(BusinessTripCarRentalExpensesProvider)
                .getBusinessTripsCarRentalExpensesByBusinessTripIdLoader.load(dbBusinessTrip.id);
        },
        summary: async (dbBusinessTrip, _, { injector }) => businessTripSummary(injector, dbBusinessTrip),
        uncategorizedTransactions: async (dbBusinessTrip, _, { injector }) => {
            const transactions = await injector
                .get(BusinessTripExpensesProvider)
                .getTransactionsByBusinessTripId(dbBusinessTrip.id);
            const transactionCategorizeInfo = await Promise.all(transactions.map(async (transaction) => {
                const matchInfo = await getTransactionMatchedAmount(injector, transaction).catch(_ => ({
                    isFullyMatched: false,
                    amount: 0,
                    errors: ["Error matching transaction's amount"],
                }));
                return {
                    transaction,
                    matchInfo,
                };
            }));
            const uncategorizedTransactions = transactionCategorizeInfo.filter(({ matchInfo }) => {
                return !matchInfo.isFullyMatched;
            });
            return uncategorizedTransactions.map(({ transaction, matchInfo }) => ({
                transaction: transaction.id,
                categorizedAmount: formatFinancialAmount(matchInfo.amount, transaction.currency),
                errors: matchInfo.errors ?? [],
            }));
        },
    },
    BusinessTripCharge: {
        businessTrip: async (dbCharge, _, { injector }) => {
            try {
                return injector
                    .get(BusinessTripsProvider)
                    .getBusinessTripsByChargeIdLoader.load(dbCharge.id)
                    .then(businessTrip => businessTrip ?? null);
            }
            catch (e) {
                console.error(`Error finding business trip for charge id ${dbCharge.id}:`, e);
                throw new GraphQLError(`Error finding business trip for charge id ${dbCharge.id}`);
            }
        },
    },
};
//# sourceMappingURL=business-trips.resolver.js.map