import { GraphQLError } from 'graphql';
import { formatFinancialAmount } from '../../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../../admin-context/providers/admin-context.provider.js';
import { FinancialEntitiesProvider } from '../../../financial-entities/providers/financial-entities.provider.js';
import { LedgerProvider } from '../../../ledger/providers/ledger.provider.js';
import { decorateLedgerRecords, getProfitLossReportAmounts, } from '../../helpers/profit-and-loss.helper.js';
import { calculateCumulativeRnDExpenses, calculateTaxAmounts } from '../../helpers/tax.helper.js';
export const taxReport = async (_, { reportYear, referenceYears }, context) => {
    const { injector } = context;
    referenceYears = referenceYears.filter(year => year !== reportYear);
    const years = [reportYear, ...referenceYears];
    years.map(year => {
        if (year < 2000 || year > new Date().getFullYear()) {
            throw new GraphQLError('Invalid year');
        }
    });
    const from = new Date(Math.min(...years) - 2, 0, 1, 0, 0, 0, 1); // Note: take 2 years before the earliest year requested, to calculate R&D spread over 3 years
    const to = new Date(Math.max(...years) + 1, 0, 0);
    const ledgerRecords = await injector
        .get(LedgerProvider)
        .getLedgerRecordsByDates({ fromDate: from, toDate: to });
    const financialEntities = await injector.get(FinancialEntitiesProvider).getAllFinancialEntities();
    const financialEntitiesDict = new Map(financialEntities.map(entity => [entity.id, entity]));
    const decoratedLedgerByYear = new Map();
    for (let year = from.getFullYear(); year <= to.getFullYear(); year++) {
        if (from.getFullYear() > to.getFullYear()) {
            break;
        }
        decoratedLedgerByYear.set(year, []);
    }
    ledgerRecords.map(record => {
        const year = record.invoice_date.getFullYear();
        const [decoratedRecord] = decorateLedgerRecords([record], financialEntitiesDict);
        decoratedLedgerByYear.get(year)?.push(decoratedRecord);
    });
    const profitLossByYear = new Map();
    const yearlyReports = [];
    for (const year of years) {
        const decoratedLedgerRecords = decoratedLedgerByYear.get(year) ?? [];
        let profitAndLoss = profitLossByYear.get(year);
        if (!profitAndLoss) {
            profitAndLoss = getProfitLossReportAmounts(decoratedLedgerRecords);
            profitLossByYear.set(year, profitAndLoss);
        }
        const { profitBeforeTax, researchAndDevelopmentExpenses } = profitAndLoss;
        const adjustedResearchAndDevelopmentExpensesAmount = researchAndDevelopmentExpenses.amount * -1;
        const cumulativeResearchAndDevelopmentExpensesAmount = calculateCumulativeRnDExpenses(year, decoratedLedgerByYear, profitLossByYear);
        const taxableCumulativeResearchAndDevelopmentExpensesAmount = cumulativeResearchAndDevelopmentExpensesAmount / 3;
        const { researchAndDevelopmentExpensesForTax, fines, untaxableGifts, businessTripsExcessExpensesAmount, salaryExcessExpensesAmount, reserves, nontaxableLinkage, taxableIncomeAmount, taxRate, specialTaxableIncome, specialTaxRate, annualTaxExpenseAmount, } = await calculateTaxAmounts(injector, year, decoratedLedgerRecords, adjustedResearchAndDevelopmentExpensesAmount, taxableCumulativeResearchAndDevelopmentExpensesAmount, profitBeforeTax.amount);
        yearlyReports.push({
            year,
            profitBeforeTax,
            researchAndDevelopmentExpensesByRecords: {
                amount: adjustedResearchAndDevelopmentExpensesAmount,
                records: researchAndDevelopmentExpenses.records.map(record => ({
                    ...record,
                    amount: record.amount * -1,
                    records: record.records.map(subRecord => ({
                        ...subRecord,
                        amount: subRecord.amount * -1,
                    })),
                })),
            },
            researchAndDevelopmentExpensesForTax,
            fines,
            untaxableGifts,
            businessTripsExcessExpensesAmount,
            salaryExcessExpensesAmount,
            reserves,
            nontaxableLinkage,
            taxableIncome: taxableIncomeAmount,
            taxRate,
            specialTaxableIncome,
            specialTaxRate,
            annualTaxExpense: annualTaxExpenseAmount,
        });
    }
    return {
        id: `tax-report-${reportYear}`,
        report: yearlyReports.find(report => report.year === reportYear),
        reference: yearlyReports.filter(report => report.year !== reportYear),
    };
};
export const taxReportYearMapper = {
    id: parent => `profit-and-loss-year-${parent.year}`,
    year: parent => parent.year,
    profitBeforeTax: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.profitBeforeTax.amount, defaultLocalCurrency),
            records: parent.profitBeforeTax.records,
        };
    },
    researchAndDevelopmentExpensesByRecords: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.researchAndDevelopmentExpensesByRecords.amount, defaultLocalCurrency),
            records: parent.researchAndDevelopmentExpensesByRecords.records,
        };
    },
    researchAndDevelopmentExpensesForTax: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return formatFinancialAmount(parent.researchAndDevelopmentExpensesForTax, defaultLocalCurrency);
    },
    fines: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.fines.amount, defaultLocalCurrency),
            records: parent.fines.records,
        };
    },
    untaxableGifts: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.untaxableGifts.amount, defaultLocalCurrency),
            records: parent.untaxableGifts.records,
        };
    },
    businessTripsExcessExpensesAmount: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return formatFinancialAmount(parent.businessTripsExcessExpensesAmount, defaultLocalCurrency);
    },
    salaryExcessExpensesAmount: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return formatFinancialAmount(parent.salaryExcessExpensesAmount, defaultLocalCurrency);
    },
    reserves: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.reserves.amount, defaultLocalCurrency),
            records: parent.reserves.records,
        };
    },
    nontaxableLinkage: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.nontaxableLinkage.amount, defaultLocalCurrency),
            records: parent.nontaxableLinkage.records,
        };
    },
    taxableIncome: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return formatFinancialAmount(parent.taxableIncome, defaultLocalCurrency);
    },
    taxRate: parent => parent.taxRate,
    specialTaxableIncome: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return {
            amount: formatFinancialAmount(parent.specialTaxableIncome.amount, defaultLocalCurrency),
            records: parent.specialTaxableIncome.records,
        };
    },
    specialTaxRate: parent => parent.specialTaxRate,
    annualTaxExpense: async (parent, _, { injector }) => {
        const { defaultLocalCurrency } = await injector
            .get(AdminContextProvider)
            .getVerifiedAdminContext();
        return formatFinancialAmount(parent.annualTaxExpense, defaultLocalCurrency);
    },
};
//# sourceMappingURL=tax-report.js.map