import { GraphQLError } from 'graphql';
import xlsx from 'node-xlsx';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { ChargesProvider } from '../../charges/providers/charges.provider.js';
import { EmployeesProvider } from '../providers/employees.provider.js';
import { FundsProvider } from '../providers/funds.provider.js';
import { SalariesProvider } from '../providers/salaries.provider.js';
export class SalaryError extends Error {
    constructor(message) {
        super(message);
    }
}
function getFormattedSalaryMonth(rawDate) {
    const dateParts = rawDate.split('/');
    return `${dateParts[1]}-${dateParts[0]}`;
}
function getEmployeeIdsFromSheet(dataArray) {
    return dataArray
        .map((data, index) => [data, index])
        .filter(([data, index]) => {
        if (index < 2)
            return false;
        if (!data || data === '')
            return false;
        return true;
    });
}
function normalizeNationalId(nationalId) {
    const stringNationalId = nationalId.toString();
    return stringNationalId.length === 9
        ? stringNationalId
        : '0'.repeat(9 - stringNationalId.length) + stringNationalId;
}
function validateGenericSalaryData(salaryData) {
    if (!salaryData) {
        throw new SalaryError('Missing salary data sheet');
    }
    return salaryData;
}
function validateRowByCategory(data, category) {
    const rowNum = data.findIndex(row => Array.isArray(row) && row[1] === category);
    if (!data[rowNum]) {
        throw new SalaryError(`Missing ${category} in row ${rowNum + 1}`);
    }
    if (!Array.isArray(data[rowNum])) {
        throw new SalaryError(`Invalid row for ${category} in row ${rowNum + 1}`);
    }
    if (!data[rowNum][1] || data[rowNum][1] !== category) {
        throw new SalaryError(`Invalid category for ${category} in row ${rowNum + 1}`);
    }
    return { row: data[rowNum], rowNum };
}
function validateColumn(row, columnNum, category, rowNum, nullable = false) {
    const columns = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    if (!nullable && row[columnNum] == null) {
        throw new SalaryError(`Missing column ${columns[columnNum]} for ${category} in row ${rowNum + 1}`);
    }
    return { cell: row[columnNum], colLetter: columns[columnNum] };
}
function validateCell(salaryData, category, columnNum, nullable = false) {
    const { row, rowNum } = validateRowByCategory(salaryData, category);
    const { cell, colLetter } = validateColumn(row, columnNum, category, rowNum, nullable);
    return { cell, colLetter, rowNum };
}
function validateNumericCell(salaryData, category, columnNum, nullable = false) {
    const { cell, colLetter, rowNum } = validateCell(salaryData, category, columnNum, nullable);
    if (typeof cell !== 'number') {
        throw new SalaryError(`Invalid ${category} in row ${rowNum + 1}, column ${colLetter}: ${cell}`);
    }
    return cell;
}
export const insertSalaryRecordsFromFile = async (_, { file, chargeId }, { injector }) => {
    try {
        const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext();
        const buffer = await file.arrayBuffer();
        const workSheetsFromFile = xlsx.parse(buffer);
        if (!workSheetsFromFile) {
            throw new SalaryError('No salary data file found');
        }
        const salaryRawData = workSheetsFromFile.find(sheet => sheet.name === 'ריכוז נתוני שכר')?.data;
        if (!salaryRawData) {
            throw new SalaryError('No salary data sheet found in file');
        }
        const rawSalaryMonth = salaryRawData[1][3];
        const salaryMonth = getFormattedSalaryMonth(rawSalaryMonth);
        const salaryData = validateGenericSalaryData(salaryRawData);
        const { row: employeesRow } = validateRowByCategory(salaryData, 'מספר זהות');
        const employeesNationalIds = getEmployeeIdsFromSheet(employeesRow);
        const allEmployeesPromise = injector
            .get(EmployeesProvider)
            .getEmployeesByEmployerLoader.load(ownerId)
            .catch(e => {
            console.error(e);
            throw new SalaryError('Failed to get employees for employer');
        });
        const salaryChargePromise = chargeId
            ? injector.get(ChargesProvider).getChargeByIdLoader.load(chargeId)
            : injector
                .get(ChargesProvider)
                .generateCharge({
                type: 'PAYROLL',
                ownerId,
                userDescription: `Salaries ${rawSalaryMonth}`,
            })
                .catch(e => {
                console.error(e);
                throw new SalaryError('Failed to generate salary charge');
            });
        const allFundsPromise = injector.get(FundsProvider).getAllFunds();
        const [allEmployees, salaryCharge, allFunds] = await Promise.all([
            allEmployeesPromise,
            salaryChargePromise,
            allFundsPromise,
        ]);
        if (!salaryCharge) {
            throw new SalaryError('Failed to get or generate salary charge');
        }
        const salaryRecords = [];
        for (const [nationalId, employeeColumn] of employeesNationalIds) {
            const employee = allEmployees.find(e => e.national_id === normalizeNationalId(Number(nationalId)));
            if (!employee) {
                throw new SalaryError(`Employee with national id ${nationalId} not found`);
            }
            function validateNumericCellWrapper(category, nullable) {
                return validateNumericCell(salaryData, category, employeeColumn, nullable);
            }
            function validateFund(category, nullable) {
                const { rowNum: prevRowNum } = validateRowByCategory(salaryData, category);
                const rowNum = prevRowNum + 1;
                const row = salaryData[rowNum];
                if (!Array.isArray(row)) {
                    throw new SalaryError(`Invalid row for ${category} in row ${rowNum + 2}`);
                }
                const { cell: fundKey, colLetter } = validateColumn(row, employeeColumn, category, rowNum, nullable);
                if (typeof fundKey !== 'number') {
                    throw new SalaryError(`Invalid ${category} in row ${rowNum + 1}, column ${colLetter}: ${fundKey}`);
                }
                const fundId = allFunds.find(f => Number(f.otsar_id) === fundKey)?.id;
                if (!fundId) {
                    throw new SalaryError(`Fund with key ${fundKey} not found`);
                }
                return fundId;
            }
            const record = {
                ownerId: salaryCharge.owner_id,
                month: salaryMonth,
                chargeId: salaryCharge.id,
                employeeId: employee.business_id,
                employer: employee.employer,
                employee: employee.national_id,
                baseSalary: validateNumericCellWrapper('שכר יסוד'),
                globalAdditionalHours: validateNumericCellWrapper('שעות נוספות גלובליות', true),
                hourlyRate: validateNumericCellWrapper('Hourly rate', true),
                bonus: validateNumericCellWrapper('בונוס', true),
                gift: validateNumericCellWrapper('מתנות', true),
                recovery: validateNumericCellWrapper('הבראה', true),
                vacationTakeout: validateNumericCellWrapper('פדיון חופשה', true),
                zkufot: validateNumericCellWrapper('שווי קרן השתלמות', true) +
                    validateNumericCellWrapper('שווי קיצבה', true),
                travelAndSubsistence: validateNumericCellWrapper('החזר הוצאות אש"ל', true),
                directPaymentAmount: validateNumericCellWrapper('נטו לתשלום'),
                socialSecurityAmountEmployee: validateNumericCellWrapper('ביטוח לאומי עובד'),
                socialSecurityAmountEmployer: validateNumericCellWrapper('ביטוח לאומי מעסיק'),
                healthPaymentAmount: validateNumericCellWrapper('דמי בריאות'),
                taxAmount: validateNumericCellWrapper('מס הכנסה'),
                trainingFundId: validateFund('קרן השתלמות'),
                trainingFundEmployeeAmount: validateNumericCellWrapper('ניכוי קה"ש עובד'),
                trainingFundEmployeePercentage: validateNumericCellWrapper('אחוז קה"ש עובד') * 100,
                trainingFundEmployerAmount: validateNumericCellWrapper('סכום קה"ש מעסיק'),
                trainingFundEmployerPercentage: validateNumericCellWrapper('אחוז קה"ש מעסיק') * 100,
                pensionFundId: validateFund('קרן פנסיה'),
                pensionEmployeeAmount: validateNumericCellWrapper('ניכוי פנסיה עובד'),
                pensionEmployeePercentage: validateNumericCellWrapper('אחוז פנסיה עובד') * 100,
                pensionEmployerAmount: validateNumericCellWrapper('סכום פנסיה מעסיק'),
                pensionEmployerPercentage: validateNumericCellWrapper('אחוז פנסיה מעסיק') * 100,
                compensationsEmployerAmount: validateNumericCellWrapper('סכום פיצויים'),
                compensationsEmployerPercentage: validateNumericCellWrapper('אחוז פיצויים') * 100,
                workDays: validateNumericCellWrapper('ימי עבודה'),
                hours: validateNumericCellWrapper('שעות עבודה'),
                jobPercentage: validateNumericCellWrapper('Job percentage') * 100,
                vacationDaysBalance: -1 * validateNumericCellWrapper('ניצול חופשה', true),
                sicknessDaysBalance: -1 * validateNumericCellWrapper('ניצול מחלה', true),
                // not currently in use:
                addedVacationDays: null,
            };
            salaryRecords.push(record);
        }
        await injector
            .get(SalariesProvider)
            .insertSalaryRecords({ salaryRecords })
            .catch(e => {
            console.error(e);
            throw new SalaryError('Failed to insert salary records from file');
        });
        return true;
    }
    catch (e) {
        if (e instanceof SalaryError) {
            throw new GraphQLError(e.message);
        }
        console.error(e);
        throw new GraphQLError('Failed to insert salary records from file');
    }
};
//# sourceMappingURL=insert-salary-records-from-file.resolvers.js.map