import { GraphQLError } from 'graphql';
import { formatFinancialAmount } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { ScopeProvider } from '../../auth/providers/scope.provider.js';
import { ChargesProvider } from '../../charges/providers/charges.provider.js';
import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js';
import { SalariesProvider } from '../providers/salaries.provider.js';
import { insertSalaryRecordsFromFile } from './insert-salary-records-from-file.resolvers.js';
import { insertSalaryRecords } from './insert-salary.resolver.js';
import { updateSalaryRecord } from './update-salary.resolver.js';
// Resolve a salary record's local currency from its OWNING business so
// multi-business reads format each record correctly; falls back to the
// request's primary business currency when the per-business value is absent.
async function salaryLocalCurrency(injector, ownerId) {
    if (ownerId) {
        const currency = await injector
            .get(ScopeProvider)
            .getBusinessPreference(ownerId, 'defaultLocalCurrency');
        if (currency) {
            return currency;
        }
    }
    const { defaultLocalCurrency } = await injector
        .get(AdminContextProvider)
        .getVerifiedAdminContext();
    return defaultLocalCurrency;
}
export const salariesResolvers = {
    Query: {
        salaryRecordsByCharge: async (_, { chargeId }, { injector }) => {
            const salaryRecords = await injector
                .get(SalariesProvider)
                .getSalaryRecordsByChargeIdLoader.load(chargeId);
            return salaryRecords;
        },
        salaryRecordsByDates: async (_, { fromDate, toDate, employeeIDs }, { injector }) => {
            const fromDateMonth = fromDate.slice(0, 7);
            const toDateMonth = toDate.slice(0, 7);
            try {
                const salaryRecords = await injector
                    .get(SalariesProvider)
                    .getSalaryRecordsByDates({ fromDate: fromDateMonth, toDate: toDateMonth });
                return salaryRecords
                    .filter(record => (employeeIDs ? employeeIDs.includes(record.employee_id) : true))
                    .sort((a, b) => a.month.localeCompare(b.month));
            }
            catch (e) {
                throw new GraphQLError(`Failed to get salary records by dates: ${e.message}`);
            }
        },
    },
    Mutation: {
        insertSalaryRecords,
        updateSalaryRecord,
        insertOrUpdateSalaryRecords: async (_, params, { injector }) => {
            try {
                const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext();
                const recordsPromises = params.salaryRecords.map(async (salaryRecord) => {
                    try {
                        const res = await injector.get(SalariesProvider).updateSalaryRecord(salaryRecord);
                        if (res.length !== 1) {
                            throw new Error('Failed to update salary record');
                        }
                        return res[0];
                    }
                    catch (e) {
                        const message = `Failed to update salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`;
                        console.error(`${message}: ${e}`);
                    }
                    try {
                        const salaryRecordToUpdate = {
                            ...salaryRecord,
                            ownerId,
                            employee: salaryRecord.employee ?? null,
                            addedVacationDays: salaryRecord.addedVacationDays ?? null,
                            baseSalary: salaryRecord.baseSalary ?? null,
                            bonus: salaryRecord.bonus ?? null,
                            chargeId: salaryRecord.chargeId ?? null,
                            compensationsEmployerAmount: salaryRecord.compensationsEmployerAmount ?? null,
                            compensationsEmployerPercentage: salaryRecord.compensationsEmployerPercentage ?? null,
                            gift: salaryRecord.gift ?? null,
                            globalAdditionalHours: salaryRecord.globalAdditionalHours ?? null,
                            healthPaymentAmount: salaryRecord.healthPaymentAmount ?? null,
                            hourlyRate: salaryRecord.hourlyRate ?? null,
                            hours: salaryRecord.hours ?? null,
                            jobPercentage: salaryRecord.jobPercentage ?? null,
                            pensionEmployeeAmount: salaryRecord.pensionEmployeeAmount ?? null,
                            pensionEmployeePercentage: salaryRecord.pensionEmployeePercentage ?? null,
                            pensionEmployerAmount: salaryRecord.pensionEmployerAmount ?? null,
                            pensionEmployerPercentage: salaryRecord.pensionEmployerPercentage ?? null,
                            pensionFundId: salaryRecord.pensionFundId ?? null,
                            recovery: salaryRecord.recovery ?? null,
                            sicknessDaysBalance: salaryRecord.sicknessDaysBalance ?? null,
                            socialSecurityAmountEmployee: salaryRecord.socialSecurityAmountEmployee ?? null,
                            socialSecurityAmountEmployer: salaryRecord.socialSecurityAmountEmployer ?? null,
                            taxAmount: salaryRecord.taxAmount ?? null,
                            trainingFundEmployeeAmount: salaryRecord.trainingFundEmployeeAmount ?? null,
                            trainingFundEmployeePercentage: salaryRecord.trainingFundEmployeePercentage ?? null,
                            trainingFundEmployerAmount: salaryRecord.trainingFundEmployerAmount ?? null,
                            trainingFundEmployerPercentage: salaryRecord.trainingFundEmployerPercentage ?? null,
                            trainingFundId: salaryRecord.trainingFundId ?? null,
                            travelAndSubsistence: salaryRecord.travelAndSubsistence ?? null,
                            vacationDaysBalance: salaryRecord.vacationDaysBalance ?? null,
                            vacationTakeout: salaryRecord.vacationTakeout ?? null,
                            workDays: salaryRecord.workDays ?? null,
                            zkufot: salaryRecord.zkufot ?? null,
                        };
                        const res = await injector
                            .get(SalariesProvider)
                            .insertSalaryRecords({ salaryRecords: [salaryRecordToUpdate] });
                        if (res.length !== 1) {
                            throw new Error('Failed to insert salary record');
                        }
                        return res[0];
                    }
                    catch (e) {
                        const message = `Failed to insert salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`;
                        console.error(`${message}: ${e}`);
                    }
                    throw new Error(`Failed to update salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`);
                });
                const updatedRecords = await Promise.all(recordsPromises);
                return {
                    salaryRecords: updatedRecords,
                };
            }
            catch (e) {
                return {
                    __typename: 'CommonError',
                    message: e?.message ??
                        e?.errors.map(e => e.message).toString() ??
                        'Unknown error',
                };
            }
        },
        insertSalaryRecordsFromFile,
    },
    UpdateSalaryRecordResult: {
        __resolveType: obj => {
            if ('__typename' in obj && obj.__typename === 'CommonError')
                return 'CommonError';
            return 'UpdateSalaryRecordSuccessfulResult';
        },
    },
    InsertSalaryRecordsResult: {
        __resolveType: obj => {
            if ('__typename' in obj && obj.__typename === 'CommonError')
                return 'CommonError';
            return 'InsertSalaryRecordsSuccessfulResult';
        },
    },
    SalaryCharge: {
        salaryRecords: (DbCharge, _, { injector }) => {
            return injector.get(SalariesProvider).getSalaryRecordsByChargeIdLoader.load(DbCharge.id);
        },
        employees: async (DbCharge, _, { injector }) => {
            const salaryRecords = await injector
                .get(SalariesProvider)
                .getSalaryRecordsByChargeIdLoader.load(DbCharge.id);
            return injector
                .get(BusinessesProvider)
                .getBusinessByIdLoader.loadMany(salaryRecords.map(salaryRecord => salaryRecord.employee_id))
                .then(res => res.map(employee => {
                if (!employee || employee instanceof Error) {
                    throw new GraphQLError(`Employee not found for one of the salary record of charge ${DbCharge.id}`);
                }
                return employee;
            }) ?? []);
        },
    },
    Salary: {
        directAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.direct_payment_amount, defaultLocalCurrency);
        },
        baseAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return DbSalary.base_salary
                ? formatFinancialAmount(DbSalary.base_salary, defaultLocalCurrency)
                : null;
        },
        employee: (DbSalary, _, { injector }) => injector
            .get(BusinessesProvider)
            .getBusinessByIdLoader.load(DbSalary.employee_id)
            .then(res => res ?? null),
        employer: (DbSalary, _, { injector }) => injector
            .get(BusinessesProvider)
            .getBusinessByIdLoader.load(DbSalary.employer)
            .then(res => res ?? null),
        pensionFund: (DbSalary, _, { injector }) => DbSalary.pension_fund_id
            ? injector
                .get(BusinessesProvider)
                .getBusinessByIdLoader.load(DbSalary.pension_fund_id)
                .then(res => res ?? null)
            : null,
        pensionEmployeeAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.pension_employee_amount, defaultLocalCurrency);
        },
        pensionEmployeePercentage: DbSalary => DbSalary.pension_employee_percentage,
        pensionEmployerAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.pension_employer_amount, defaultLocalCurrency);
        },
        pensionEmployerPercentage: DbSalary => DbSalary.pension_employer_percentage,
        compensationsAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.compensations_employer_amount, defaultLocalCurrency);
        },
        compensationsPercentage: DbSalary => DbSalary.compensations_employer_percentage,
        trainingFund: (DbSalary, _, { injector }) => DbSalary.training_fund_id
            ? injector
                .get(BusinessesProvider)
                .getBusinessByIdLoader.load(DbSalary.training_fund_id)
                .then(res => res ?? null)
            : null,
        trainingFundEmployeeAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.training_fund_employee_amount, defaultLocalCurrency);
        },
        trainingFundEmployeePercentage: DbSalary => DbSalary.training_fund_employee_percentage,
        trainingFundEmployerAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.training_fund_employer_amount, defaultLocalCurrency);
        },
        trainingFundEmployerPercentage: DbSalary => DbSalary.training_fund_employer_percentage,
        socialSecurityEmployeeAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.social_security_amount_employee, defaultLocalCurrency);
        },
        socialSecurityEmployerAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.social_security_amount_employer, defaultLocalCurrency);
        },
        incomeTaxAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.tax_amount, defaultLocalCurrency);
        },
        healthInsuranceAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.health_payment_amount, defaultLocalCurrency);
        },
        globalAdditionalHoursAmount: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.global_additional_hours, defaultLocalCurrency);
        },
        bonus: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.bonus, defaultLocalCurrency);
        },
        gift: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.gift, defaultLocalCurrency);
        },
        travelAndSubsistence: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.travel_and_subsistence, defaultLocalCurrency);
        },
        recovery: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.recovery, defaultLocalCurrency);
        },
        vacationTakeout: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.vacation_takeout, defaultLocalCurrency);
        },
        notionalExpense: async (DbSalary, _, { injector }) => {
            const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id);
            return formatFinancialAmount(DbSalary.zkufot, defaultLocalCurrency);
        },
        vacationDays: DbSalary => ({
            added: DbSalary.added_vacation_days ? Number(DbSalary.added_vacation_days) : null,
            balance: DbSalary.vacation_days_balance ? Number(DbSalary.vacation_days_balance) : null,
        }),
        workDays: DbSalary => (DbSalary.work_days ? Number(DbSalary.work_days) : null),
        sicknessDays: DbSalary => ({
            balance: DbSalary.sickness_days_balance ? Number(DbSalary.sickness_days_balance) : null,
        }),
        charge: (DbSalary, _, { injector }) => {
            if (!DbSalary.charge_id) {
                return null;
            }
            return injector
                .get(ChargesProvider)
                .getChargeByIdLoader.load(DbSalary.charge_id)
                .then(res => res ?? null);
        },
    },
};
//# sourceMappingURL=salaries.resolvers.js.map