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 getAllDocuments = sql `
  SELECT *
  FROM accounter_schema.documents
  ORDER BY created_at DESC;
`;
const getDocumentsByChargeId = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE charge_id in $$chargeIds
  ORDER BY created_at DESC;
`;
const getDocumentsByIds = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE id IN $$Ids;
`;
const getDocumentsByBusinessIds = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE debtor_id IN $$Ids OR creditor_id IN $$Ids;
`;
const getDocumentsByHashes = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE file_hash IN $$hashes;
`;
const getDocumentsByMissingRequiredInfo = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE charge_id IS NOT NULL
  AND (image_url IS NULL AND file_url IS NULL) -- missing link
  OR (type <> 'OTHER'
      AND (serial_number IS NULL
          OR date IS NULL
          OR total_amount IS NULL
          OR currency_code IS NULL
          OR vat_amount IS NULL
          OR debtor_id IS NULL
          OR creditor_id IS NULL));
`;
const updateDocument = sql `
  UPDATE accounter_schema.documents
  SET
  charge_id = CASE
    WHEN $chargeId='00000000-0000-0000-0000-000000000000' THEN NULL
    ELSE COALESCE(
      $chargeId::UUID,
      charge_id,
      NULL
    ) END,
  currency_code = COALESCE(
    $currencyCode,
    currency_code,
    NULL
  ),
  date = COALESCE(
    $date,
    date,
    NULL
  ),
  file_url = COALESCE(
    $fileUrl,
    file_url,
    NULL
  ),
  id = COALESCE(
    id,
    id,
    NULL
  ),
  image_url = COALESCE(
    $imageUrl,
    image_url,
    NULL
  ),
  updated_at = NOW(),
  serial_number = COALESCE(
    $serialNumber,
    serial_number,
    NULL
  ),
  total_amount = COALESCE(
    $totalAmount,
    total_amount,
    NULL
  ),
  type = COALESCE(
    $type,
    type,
    NULL
  ),
  vat_amount = COALESCE(
    $vatAmount,
    vat_amount,
    NULL
  ),
  is_reviewed = COALESCE(
    $isReviewed,
    is_reviewed
  ),
  creditor_id = COALESCE(
    $creditorId,
    creditor_id
  ),
  debtor_id = COALESCE(
    $debtorId,
    debtor_id
  ),
  no_vat_amount = COALESCE(
    $noVatAmount,
    no_vat_amount
  ),
  vat_report_date_override = COALESCE(
    $vatReportDateOverride,
    vat_report_date_override
  ),
  allocation_number = COALESCE(
    $allocationNumber,
    allocation_number
  ),
  exchange_rate_override = COALESCE(
    $exchangeRateOverride,
    exchange_rate_override
  ),
  description = COALESCE(
    $description,
    description
  ),
  remarks = COALESCE(
    $remarks,
    remarks
  )
  WHERE
    id = $documentId
  RETURNING *;
`;
const deleteDocument = sql `
  DELETE FROM accounter_schema.documents
  WHERE id = $documentId
  RETURNING id;
`;
const insertDocuments = sql `
    INSERT INTO accounter_schema.documents (
      image_url,
      file_url,
      type,
      serial_number,
      date,
      total_amount,
      currency_code,
      vat_amount,
      charge_id,
      vat_report_date_override,
      no_vat_amount,
      creditor_id,
      debtor_id,
      allocation_number,
      exchange_rate_override,
      file_hash,
      description,
      remarks,
      owner_id
    )
    VALUES $$documents(
      image,
      file,
      documentType,
      serialNumber,
      date,
      amount,
      currencyCode,
      vat,
      chargeId,
      vatReportDateOverride,
      noVatAmount,
      creditorId,
      debtorId,
      allocationNumber,
      exchangeRateOverride,
      fileHash,
      description,
      remarks,
      ownerId
    )
    RETURNING *;`;
const getDocumentsByFilters = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE
    ($isIDs = 0 OR id IN $$IDs)
    AND ($fromVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE >= date_trunc('day', $fromVatDate ::DATE))
    AND ($toVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE <= date_trunc('day', $toVatDate ::DATE))
    AND ($isBusinessIDs = 0 OR debtor_id IN $$businessIDs OR creditor_id IN $$businessIDs)
  ORDER BY created_at DESC;
`;
const getDocumentsByExtendedFilters = sql `
  SELECT *
  FROM accounter_schema.documents
  WHERE
    ($isIDs = 0 OR id IN $$IDs)
    AND ($fromVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE >= date_trunc('day', $fromVatDate ::DATE))
    AND ($toVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE <= date_trunc('day', $toVatDate ::DATE))
    AND ($fromDate ::TEXT IS NULL OR date::TEXT::DATE >= date_trunc('day', $fromDate ::DATE))
    AND ($toDate ::TEXT IS NULL OR date::TEXT::DATE <= date_trunc('day', $toDate ::DATE))
    AND ($isBusinessIDs = 0 OR debtor_id IN $$businessIDs OR creditor_id IN $$businessIDs)
    AND ($isOwnerIDs = 0 OR owner_id IN $$ownerIDs)
    AND ($isUnmatched = 0 OR NOT EXISTS (
      SELECT 1 
      FROM accounter_schema.transactions t 
      WHERE t.charge_id = charge_id
    ))
  ORDER BY created_at DESC;
`;
const replaceDocumentsChargeId = sql `
  UPDATE accounter_schema.documents
  SET charge_id = $assertChargeID
  WHERE charge_id = $replaceChargeID
  RETURNING id;
`;
let DocumentsProvider = class DocumentsProvider {
    db;
    adminContextProvider;
    constructor(db, adminContextProvider) {
        this.db = db;
        this.adminContextProvider = adminContextProvider;
    }
    allDocumentsCache = null;
    async getAllDocuments(params) {
        if (this.allDocumentsCache) {
            return this.allDocumentsCache;
        }
        this.allDocumentsCache = getAllDocuments.run(params, this.db).then(docs => {
            docs.map(doc => {
                this.getDocumentsByIdLoader.prime(doc.id, doc);
            });
            return docs;
        });
        return this.allDocumentsCache;
    }
    async batchDocumentsByChargeIds(chargeIds) {
        const uniqueIDs = [...new Set(chargeIds)];
        try {
            const docs = await getDocumentsByChargeId.run({ chargeIds: uniqueIDs }, this.db);
            return chargeIds.map(id => docs.filter(doc => {
                this.getDocumentsByIdLoader.prime(doc.id, doc);
                return doc.charge_id === id;
            }));
        }
        catch (e) {
            console.error(e);
            return chargeIds.map(() => []);
        }
    }
    getDocumentsByChargeIdLoader = new DataLoader((keys) => this.batchDocumentsByChargeIds(keys));
    getDocumentsByFilters(params) {
        const isIDs = !!params?.IDs?.filter(Boolean).length;
        const isBusinessIDs = !!params?.businessIDs?.filter(Boolean).length;
        const fullParams = {
            isIDs: isIDs ? 1 : 0,
            isBusinessIDs: isBusinessIDs ? 1 : 0,
            fromVatDate: null,
            toVatDate: null,
            ...params,
            IDs: isIDs ? params.IDs : [null],
            businessIDs: isBusinessIDs ? params.businessIDs : [null],
        };
        return getDocumentsByFilters.run(fullParams, this.db).then(docs => {
            docs.map(doc => {
                this.getDocumentsByIdLoader.prime(doc.id, doc);
            });
            return docs;
        });
    }
    getDocumentsByExtendedFilters(params) {
        const isIDs = !!params?.IDs?.filter(Boolean).length;
        const isBusinessIDs = !!params?.businessIDs?.filter(Boolean).length;
        const isOwnerIDs = !!params?.ownerIDs?.filter(Boolean).length;
        const fullParams = {
            isIDs: isIDs ? 1 : 0,
            isBusinessIDs: isBusinessIDs ? 1 : 0,
            isOwnerIDs: isOwnerIDs ? 1 : 0,
            fromVatDate: null,
            toVatDate: null,
            ...params,
            isUnmatched: params.unmatched ? 1 : 0,
            IDs: isIDs ? params.IDs : [null],
            businessIDs: isBusinessIDs ? params.businessIDs : [null],
            ownerIDs: isOwnerIDs ? params.ownerIDs : [null],
        };
        return getDocumentsByExtendedFilters.run(fullParams, this.db);
    }
    async batchDocumentsByIds(ids) {
        const uniqueIDs = [...new Set(ids)];
        try {
            const docs = await getDocumentsByIds.run({ Ids: uniqueIDs }, this.db);
            return ids.map(id => docs.find(doc => doc.id === id));
        }
        catch (e) {
            console.error(e);
            return ids.map(() => null);
        }
    }
    getDocumentsByIdLoader = new DataLoader((keys) => this.batchDocumentsByIds(keys));
    async batchDocumentsByBusinessIds(businessIds) {
        const uniqueIDs = [...new Set(businessIds)];
        try {
            const docs = await getDocumentsByBusinessIds.run({ Ids: uniqueIDs }, this.db);
            docs.map(doc => {
                this.getDocumentsByIdLoader.prime(doc.id, doc);
            });
            return businessIds.map(id => docs.filter(doc => doc.creditor_id === id || doc.debtor_id === id));
        }
        catch (e) {
            console.error(e);
            return businessIds.map(() => null);
        }
    }
    getDocumentsByBusinessIdLoader = new DataLoader((businessIds) => this.batchDocumentsByBusinessIds(businessIds));
    async batchDocumentsByHash(hashes) {
        const uniqueHashes = [...new Set(hashes)];
        try {
            const docs = await getDocumentsByHashes.run({ hashes: uniqueHashes.map(hash => hash.toString()) }, this.db);
            docs.map(doc => {
                this.getDocumentsByIdLoader.prime(doc.id, doc);
            });
            return hashes.map(hash => docs.find(doc => doc.file_hash === hash.toString()));
        }
        catch (e) {
            console.error(e);
            return hashes.map(() => null);
        }
    }
    getDocumentByHash = new DataLoader((hashes) => this.batchDocumentsByHash(hashes));
    async getDocumentsByMissingRequiredInfo() {
        return getDocumentsByMissingRequiredInfo.run(undefined, this.db);
    }
    async updateDocument(params) {
        if (params.documentId) {
            const document = await this.getDocumentsByIdLoader.load(params.documentId);
            if (document?.charge_id) {
                this.getDocumentsByChargeIdLoader.clear(document.charge_id);
            }
            this.invalidateById(params.documentId);
        }
        const totalAmount = params.totalAmount == null ? undefined : Math.abs(params.totalAmount); // ensure amount is positive, as the sign is determined by debtor/creditor
        return updateDocument.run({ ...params, totalAmount }, this.db);
    }
    async deleteDocument(params) {
        if (params.documentId) {
            const document = await this.getDocumentsByIdLoader.load(params.documentId);
            if (document?.charge_id) {
                this.getDocumentsByChargeIdLoader.clear(document.charge_id);
            }
            this.invalidateById(params.documentId);
        }
        return deleteDocument.run(params, this.db);
    }
    async insertDocuments(params, dbConnection) {
        if (params.documents.length) {
            params.documents.map(doc => {
                if (doc.chargeId)
                    this.invalidateByChargeId(doc.chargeId);
            });
        }
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        const documentsWithOwnerId = params.documents.map(doc => reassureOwnerIdExists({ ...doc, amount: doc.amount == null ? undefined : Math.abs(doc.amount) }, // ensure amount is positive, as the sign is determined by debtor/creditor
        ownerId));
        return insertDocuments.run({ documents: documentsWithOwnerId }, dbConnection ?? this.db);
    }
    async replaceDocumentsChargeId(params) {
        if (params.assertChargeID) {
            this.invalidateByChargeId(params.assertChargeID);
        }
        if (params.replaceChargeID) {
            this.invalidateByChargeId(params.replaceChargeID);
        }
        return replaceDocumentsChargeId.run(params, this.db);
    }
    async invalidateById(id) {
        const document = await this.getDocumentsByIdLoader.load(id);
        if (document) {
            if (document.charge_id)
                this.getDocumentsByChargeIdLoader.clear(document.charge_id);
            if (document.file_hash)
                this.getDocumentByHash.clear(Number(document.file_hash));
            if (document.debtor_id)
                this.getDocumentsByBusinessIdLoader.clear(document.debtor_id);
            if (document.creditor_id)
                this.getDocumentsByBusinessIdLoader.clear(document.creditor_id);
        }
        this.getDocumentsByIdLoader.clear(id);
        this.allDocumentsCache = null;
    }
    async invalidateByChargeId(chargeId) {
        const documents = await this.getDocumentsByChargeIdLoader.load(chargeId);
        documents.map(doc => {
            this.getDocumentsByIdLoader.clear(doc.id);
            if (doc.debtor_id)
                this.getDocumentsByBusinessIdLoader.clear(doc.debtor_id);
            if (doc.creditor_id)
                this.getDocumentsByBusinessIdLoader.clear(doc.creditor_id);
        });
        this.getDocumentsByChargeIdLoader.clear(chargeId);
        this.allDocumentsCache = null;
    }
    clearCache() {
        this.getDocumentsByIdLoader.clearAll();
        this.getDocumentsByChargeIdLoader.clearAll();
        this.getDocumentsByBusinessIdLoader.clearAll();
        this.getDocumentByHash.clearAll();
        this.allDocumentsCache = null;
    }
};
DocumentsProvider = __decorate([
    Injectable({
        scope: Scope.Operation,
        global: true,
    }),
    __metadata("design:paramtypes", [TenantAwareDBClient,
        AdminContextProvider])
], DocumentsProvider);
export { DocumentsProvider };
//# sourceMappingURL=documents.provider.js.map