import { __decorate, __metadata } from "tslib";
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { reassureOwnerIdExists } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js';
const getExpensesByChargeIds = sql `
  SELECT *
  FROM accounter_schema.misc_expenses
  WHERE charge_id IN $$chargeIds;`;
const getExpensesByIds = sql `
  SELECT *
  FROM accounter_schema.misc_expenses
  WHERE id IN $$ids;`;
const getExpensesByFinancialEntityIds = sql `
SELECT *
FROM accounter_schema.misc_expenses
WHERE creditor_id IN $$financialEntityIds OR debtor_id IN $$financialEntityIds;`;
const updateExpense = sql `
  UPDATE accounter_schema.misc_expenses
  SET
  charge_id = COALESCE(
    $chargeId,
    charge_id
  ),
  creditor_id = COALESCE(
    $creditorId,
    creditor_id
  ),
  debtor_id = COALESCE(
    $debtorId,
    debtor_id
  ),
  amount = COALESCE(
    $amount,
    amount
  ),
  currency = COALESCE(
    $currency,
    currency
  ),
  description = COALESCE(
    $description,
    description
  ),
  invoice_date = COALESCE(
    $invoiceDate,
    invoice_date
  ),
  value_date = COALESCE(
    $valueDate,
    value_date
  )
  WHERE
    id = $miscExpenseId
  RETURNING *;`;
const replaceMiscExpensesChargeId = sql `
    UPDATE accounter_schema.misc_expenses
      SET
      charge_id = $assertChargeID
    WHERE
      charge_id = $replaceChargeID
    RETURNING *
  `;
const insertExpense = sql `
  INSERT INTO accounter_schema.misc_expenses (charge_id, creditor_id, debtor_id, amount, currency, description, invoice_date, value_date, owner_id)
  VALUES ($chargeId, $creditorId, $debtorId, $amount, $currency, $description, $invoiceDate, $valueDate, $ownerId)
  RETURNING *;`;
const insertExpenses = sql `
  INSERT INTO accounter_schema.misc_expenses (charge_id, creditor_id, debtor_id, amount, currency, description, invoice_date, value_date, owner_id)
  VALUES $$miscExpenses(chargeId, creditorId, debtorId, amount, currency, description, invoiceDate, valueDate, ownerId)
  RETURNING *;`;
const deleteExpense = sql `
  DELETE FROM accounter_schema.misc_expenses
  WHERE id = $id
  RETURNING id;`;
let MiscExpensesProvider = class MiscExpensesProvider {
    db;
    adminContextProvider;
    constructor(db, adminContextProvider) {
        this.db = db;
        this.adminContextProvider = adminContextProvider;
    }
    async batchExpensesByChargeIds(chargeIds) {
        const expenses = await getExpensesByChargeIds.run({ chargeIds }, this.db);
        const expensesByChargeId = new Map();
        expenses.map(expense => {
            if (expensesByChargeId.has(expense.charge_id)) {
                expensesByChargeId.get(expense.charge_id)?.push(expense);
            }
            else {
                expensesByChargeId.set(expense.charge_id, [expense]);
            }
        });
        return chargeIds.map(id => expensesByChargeId.get(id) ?? []);
    }
    getExpensesByChargeIdLoader = new DataLoader((chargeIds) => this.batchExpensesByChargeIds(chargeIds));
    async batchExpensesByFinancialEntityIds(financialEntityIds) {
        const expenses = await getExpensesByFinancialEntityIds.run({ financialEntityIds }, this.db);
        const expensesByFinancialEntityId = new Map();
        expenses.map(expense => {
            if (expensesByFinancialEntityId.has(expense.creditor_id)) {
                expensesByFinancialEntityId.get(expense.creditor_id)?.push(expense);
            }
            else {
                expensesByFinancialEntityId.set(expense.creditor_id, [expense]);
            }
            if (expensesByFinancialEntityId.has(expense.debtor_id)) {
                expensesByFinancialEntityId.get(expense.debtor_id)?.push(expense);
            }
            else {
                expensesByFinancialEntityId.set(expense.debtor_id, [expense]);
            }
        });
        return financialEntityIds.map(id => expensesByFinancialEntityId.get(id) ?? []);
    }
    getExpensesByFinancialEntityIdLoader = new DataLoader((financialEntityIds) => this.batchExpensesByFinancialEntityIds(financialEntityIds));
    async batchExpensesByIds(ids) {
        const expenses = await getExpensesByIds.run({ ids }, this.db);
        return ids.map(id => expenses.find(expense => expense.id === id));
    }
    getExpensesByIdLoader = new DataLoader((ids) => this.batchExpensesByIds(ids));
    async updateExpense(params) {
        if (params.miscExpenseId)
            await this.invalidateById(params.miscExpenseId);
        return updateExpense.run(params, this.db);
    }
    async replaceMiscExpensesChargeId(params) {
        if (params.assertChargeID) {
            await this.invalidateByChargeId(params.assertChargeID);
        }
        if (params.replaceChargeID) {
            await this.invalidateByChargeId(params.replaceChargeID);
        }
        return replaceMiscExpensesChargeId.run(params, this.db);
    }
    async insertExpense(params) {
        if (params.chargeId)
            await this.invalidateByChargeId(params.chargeId);
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        return insertExpense.run(reassureOwnerIdExists(params, ownerId), this.db);
    }
    async insertExpenses(params) {
        const chargeIds = Array.from(new Set(params.miscExpenses.map(({ chargeId }) => chargeId))).filter(id => !!id);
        if (chargeIds.length) {
            await Promise.all(chargeIds.map(chargeId => this.invalidateByChargeId(chargeId)));
        }
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        return insertExpenses.run({
            miscExpenses: params.miscExpenses.map(expense => ({
                ...expense,
                ownerId: expense.ownerId || ownerId,
            })),
        }, this.db);
    }
    async deleteMiscExpense(params) {
        if (params.id)
            await this.invalidateById(params.id);
        return deleteExpense.run(params, this.db);
    }
    async invalidateByChargeId(chargeId) {
        this.getExpensesByChargeIdLoader.clear(chargeId);
    }
    async invalidateByFinancialEntityId(financialEntityId) {
        this.getExpensesByFinancialEntityIdLoader.clear(financialEntityId);
    }
    async invalidateById(id) {
        const expense = await this.getExpensesByIdLoader.load(id);
        if (expense) {
            this.getExpensesByChargeIdLoader.clear(expense.charge_id);
            if (expense.creditor_id) {
                this.getExpensesByFinancialEntityIdLoader.clear(expense.creditor_id);
            }
            if (expense.debtor_id) {
                this.getExpensesByFinancialEntityIdLoader.clear(expense.debtor_id);
            }
        }
        this.getExpensesByIdLoader.clear(id);
    }
    clearCache() {
        this.getExpensesByIdLoader.clearAll();
        this.getExpensesByChargeIdLoader.clearAll();
        this.getExpensesByFinancialEntityIdLoader.clearAll();
    }
};
MiscExpensesProvider = __decorate([
    Injectable({
        scope: Scope.Operation,
        global: true,
    }),
    __metadata("design:paramtypes", [TenantAwareDBClient,
        AdminContextProvider])
], MiscExpensesProvider);
export { MiscExpensesProvider };
//# sourceMappingURL=misc-expenses.provider.js.map