import { __decorate, __metadata } from "tslib";
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { LedgerLockError } from '../../../shared/errors.js';
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';
import { validateLedgerRecordParams } from '../helpers/ledger-validation.helper.js';
const getLedgerRecordsByIds = sql `
SELECT *
FROM accounter_schema.ledger_records
WHERE id IN $$ids
AND owner_id = $ownerId;`;
const getLedgerRecordsByChargesIds = sql `
    SELECT *
    FROM accounter_schema.ledger_records
    WHERE charge_id IN $$chargeIds
    AND owner_id = $ownerId;`;
const getLedgerRecordsByFinancialEntityIds = sql `
    SELECT *
    FROM accounter_schema.ledger_records
    WHERE debit_entity1 IN $$financialEntityIds
      OR debit_entity2 IN $$financialEntityIds
      OR credit_entity1 IN $$financialEntityIds
      OR credit_entity1 IN $$financialEntityIds
      AND owner_id = $ownerId;`;
const getLedgerRecordsByDates = sql `
    SELECT *
    FROM accounter_schema.ledger_records
    WHERE invoice_date BETWEEN $fromDate AND $toDate
    AND owner_id = $ownerId;`;
const getLedgerBalanceToDate = sql `
    WITH grouped_entities AS (SELECT credit_entity1 AS entity_id, credit_local_amount1 AS amount, invoice_date
                              FROM accounter_schema.ledger_records
                              UNION
                              SELECT credit_entity1, credit_local_amount1, invoice_date
                              FROM accounter_schema.ledger_records
                              UNION
                              SELECT debit_entity1, debit_local_amount1 * -1, invoice_date
                              FROM accounter_schema.ledger_records
                              UNION
                              SELECT debit_entity2, debit_local_amount2 * -1, invoice_date
                              FROM accounter_schema.ledger_records)
    SELECT entity_id, sum(amount)
    FROM grouped_entities
    WHERE invoice_date < $date
      AND entity_id IS NOT NULL
    GROUP BY entity_id;`;
const updateLedgerRecord = sql `
  UPDATE accounter_schema.ledger_records
  SET
  charge_id = COALESCE(
    $chargeId,
    charge_id
  ),
  credit_entity1 = COALESCE(
    $creditEntity1,
    credit_entity1
  ),
  credit_entity2 = COALESCE(
    $creditEntity2,
    credit_entity2
  ),
  credit_foreign_amount1 = COALESCE(
    $creditForeignAmount1,
    credit_foreign_amount1
  ),
  credit_foreign_amount2 = COALESCE(
    $creditForeignAmount2,
    credit_foreign_amount2
  ),
  credit_local_amount1 = COALESCE(
    $creditLocalAmount1,
    credit_local_amount1
  ),
  credit_local_amount2 = COALESCE(
    $creditLocalAmount2,
    credit_local_amount2
  ),
  currency = COALESCE(
    $currency,
    currency
  ),
  debit_entity1 = COALESCE(
    $debitEntity1,
    debit_entity1
  ),
  debit_entity2 = COALESCE(
    $debitEntity2,
    debit_entity2
  ),
  debit_foreign_amount1 = COALESCE(
    $debitForeignAmount1,
    debit_foreign_amount1
  ),
  debit_foreign_amount2 = COALESCE(
    $debitForeignAmount2,
    debit_foreign_amount2
  ),
  debit_local_amount1 = COALESCE(
    $debitLocalAmount1,
    debit_local_amount1
  ),
  debit_local_amount2 = COALESCE(
    $debitLocalAmount2,
    debit_local_amount2
  ),
  description = COALESCE(
    $description,
    description
  ),
  invoice_date = COALESCE(
    $invoiceDate,
    invoice_date
  ),
  reference1 = COALESCE(
    $reference,
    reference1
  ),
  value_date = COALESCE(
    $valueDate,
    value_date
  )
  WHERE
    id = $ledgerId
    AND owner_id = $ownerId
  RETURNING *;
`;
const insertLedgerRecords = sql `
  INSERT INTO accounter_schema.ledger_records (
    charge_id,
    credit_entity1,
    credit_entity2,
    credit_foreign_amount1,
    credit_foreign_amount2,
    credit_local_amount1,
    credit_local_amount2,
    currency,
    debit_entity1,
    debit_entity2,
    debit_foreign_amount1,
    debit_foreign_amount2,
    debit_local_amount1,
    debit_local_amount2,
    description,
    invoice_date,
    owner_id,
    reference1,
    value_date
  )
  VALUES $$ledgerRecords(
    chargeId,
    creditEntity1,
    creditEntity2,
    creditForeignAmount1,
    creditForeignAmount2,
    creditLocalAmount1,
    creditLocalAmount2,
    currency,
    debitEntity1,
    debitEntity2,
    debitForeignAmount1,
    debitForeignAmount2,
    debitLocalAmount1,
    debitLocalAmount2,
    description,
    invoiceDate,
    ownerId,
    reference,
    valueDate
  )
  RETURNING *;
`;
const deleteLedgerRecords = sql `
  DELETE FROM accounter_schema.ledger_records
  WHERE id IN $$ledgerRecordIds
  AND owner_id = $ownerId;
`;
const deleteLedgerRecordsByChargeIds = sql `
  DELETE FROM accounter_schema.ledger_records
  WHERE charge_id IN $$chargeIds
  AND owner_id = $ownerId;
`;
const replaceLedgerRecordsChargeId = sql `
  UPDATE accounter_schema.ledger_records
    SET
    charge_id = $assertChargeID
  WHERE
    charge_id = $replaceChargeID
  RETURNING *
`;
const lockLedgerRecords = sql `
  UPDATE accounter_schema.ledger_records
    SET
    locked = TRUE
  WHERE
    invoice_date <= $date
    OR value_date <= $date
  RETURNING *
`;
let LedgerProvider = class LedgerProvider {
    db;
    adminContextProvider;
    constructor(db, adminContextProvider) {
        this.db = db;
        this.adminContextProvider = adminContextProvider;
    }
    async batchLedgerRecordsByIds(ids) {
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        const ledgerRecords = await getLedgerRecordsByIds.run({
            ids,
            ownerId,
        }, this.db);
        return ids.map(id => ledgerRecords.find(record => record.id === id));
    }
    getLedgerRecordsByIdLoader = new DataLoader((ledgerIds) => this.batchLedgerRecordsByIds(ledgerIds));
    async batchLedgerRecordsByChargesIds(ids) {
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        const ledgerRecords = await getLedgerRecordsByChargesIds.run({
            chargeIds: ids,
            ownerId,
        }, this.db);
        return ids.map(id => ledgerRecords.filter(record => record.charge_id === id));
    }
    getLedgerRecordsByChargesIdLoader = new DataLoader((keys) => this.batchLedgerRecordsByChargesIds(keys));
    async batchLedgerRecordsByFinancialEntityIds(ids) {
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        const ledgerRecords = await getLedgerRecordsByFinancialEntityIds.run({
            financialEntityIds: ids,
            ownerId,
        }, this.db);
        return ids.map(id => ledgerRecords.filter(record => [
            record.debit_entity1,
            record.debit_entity2,
            record.credit_entity1,
            record.credit_entity2,
        ].includes(id)));
    }
    getLedgerRecordsByFinancialEntityIdLoader = new DataLoader((keys) => this.batchLedgerRecordsByFinancialEntityIds(keys));
    async getLedgerRecordsByDates(params) {
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        return getLedgerRecordsByDates.run(reassureOwnerIdExists(params, ownerId), this.db);
    }
    getLedgerBalanceToDate(date) {
        return getLedgerBalanceToDate.run({ date }, this.db);
    }
    async updateLedgerRecord(params) {
        // validate non are locked
        if (params.ledgerId) {
            const record = await this.getLedgerRecordsByIdLoader.load(params.ledgerId);
            if (record?.locked) {
                throw new LedgerLockError();
            }
        }
        this.clearCache();
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        return updateLedgerRecord.run(reassureOwnerIdExists(params, ownerId), this.db);
    }
    async insertLedgerRecords(params) {
        if (params.ledgerRecords.length === 0)
            return [];
        this.clearCache();
        const { defaultLocalCurrency } = await this.adminContextProvider.getVerifiedAdminContext();
        params.ledgerRecords.map(record => validateLedgerRecordParams(record, defaultLocalCurrency));
        return insertLedgerRecords.run(params, this.db);
    }
    async deleteLedgerRecordsByIds(ids) {
        // validate non are locked
        const records = await this.getLedgerRecordsByIdLoader.loadMany(ids);
        records.map(record => {
            if (record instanceof Error) {
                throw record;
            }
            if (record?.locked) {
                throw new LedgerLockError();
            }
        });
        this.clearCache();
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        await deleteLedgerRecords.run({
            ledgerRecordIds: ids,
            ownerId,
        }, this.db);
        return ids.map(_id => void 0);
    }
    deleteLedgerRecordsByIdLoader = new DataLoader((keys) => this.deleteLedgerRecordsByIds(keys), { cache: false });
    async deleteLedgerRecordsByChargeIds(chargeIds) {
        // validate non are locked
        const records = await this.getLedgerRecordsByChargesIdLoader.loadMany(chargeIds);
        records.map(record => {
            if (record instanceof Error) {
                throw record;
            }
            if (record.some(r => r.locked)) {
                throw new LedgerLockError();
            }
        });
        this.clearCache();
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        await deleteLedgerRecordsByChargeIds.run({
            chargeIds,
            ownerId,
        }, this.db);
        return chargeIds.map(_id => void 0);
    }
    deleteLedgerRecordsByChargeIdLoader = new DataLoader((chargeIds) => this.deleteLedgerRecordsByChargeIds(chargeIds), { cache: false });
    replaceLedgerRecordsChargeId(params) {
        this.clearCache();
        return replaceLedgerRecordsChargeId.run(params, this.db);
    }
    lockLedgerRecords(date) {
        this.clearCache();
        return lockLedgerRecords.run({ date }, this.db);
    }
    clearCache() {
        this.getLedgerRecordsByIdLoader.clearAll();
        this.getLedgerRecordsByChargesIdLoader.clearAll();
        this.getLedgerRecordsByFinancialEntityIdLoader.clearAll();
    }
};
LedgerProvider = __decorate([
    Injectable({
        scope: Scope.Operation,
        global: true,
    }),
    __metadata("design:paramtypes", [TenantAwareDBClient,
        AdminContextProvider])
], LedgerProvider);
export { LedgerProvider };
//# sourceMappingURL=ledger.provider.js.map