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 getTaxCategoryByBusinessIDs = sql `
SELECT fe.id, fe.name, fe.sort_code, fe.type, fe.created_at, fe.updated_at, fe.irs_code, fe.is_active, tc.hashavshevet_name, tc.tax_excluded, tcm.business_id, tcm.owner_id
FROM accounter_schema.tax_categories tc
LEFT JOIN accounter_schema.financial_entities fe
  ON fe.id = tc.id
LEFT JOIN accounter_schema.business_tax_category_match tcm
  ON tcm.tax_category_id = tc.id
WHERE tcm.business_id IN $$BusinessIds;`;
const getTaxCategoryBySortCodes = sql `
SELECT fe.id, fe.name, fe.sort_code, fe.type, fe.created_at, fe.updated_at, fe.irs_code, fe.is_active, tc.hashavshevet_name, tc.tax_excluded, fe.owner_id
FROM accounter_schema.tax_categories tc
LEFT JOIN accounter_schema.financial_entities fe
  ON fe.id = tc.id
WHERE fe.sort_code IN $$sortCodes;`;
const getTaxCategoryByFinancialAccountIdsAndCurrencies = sql `
SELECT fe.id, fe.name, fe.sort_code, fe.type, fe.created_at, fe.updated_at, fe.owner_id, fe.irs_code, fe.is_active, tc.hashavshevet_name, tc.tax_excluded, fatc.financial_account_id, fatc.currency
FROM accounter_schema.financial_accounts_tax_categories fatc
LEFT JOIN accounter_schema.tax_categories tc
  ON fatc.tax_category_id = tc.id
LEFT JOIN accounter_schema.financial_entities fe
  ON fatc.tax_category_id = fe.id
WHERE fatc.currency IN $$Currencies
AND fatc.financial_account_id IN $$FinancialAccountIds;`;
const getTaxCategoryByFinancialAccountOwnerIds = sql `
SELECT fe.id, fe.name, fe.sort_code, fe.type, fe.created_at, fe.updated_at, fe.owner_id, fe.irs_code, fe.is_active, tc.hashavshevet_name, tc.tax_excluded, fatc.financial_account_id, fatc.currency, fa.owner as "financial_account_owner_id"
FROM accounter_schema.financial_accounts_tax_categories fatc
LEFT JOIN accounter_schema.tax_categories tc
  ON fatc.tax_category_id = tc.id
LEFT JOIN accounter_schema.financial_entities fe
  ON fatc.tax_category_id = fe.id
LEFT JOIN accounter_schema.financial_accounts fa
  ON fa.id = fatc.financial_account_id
WHERE fa.owner IN $$ownerIds;`;
const getTaxCategoryByFinancialAccountIds = sql `
SELECT fe.id, fe.name, fe.sort_code, fe.type, fe.created_at, fe.updated_at, fe.owner_id, fe.irs_code, fe.is_active, tc.hashavshevet_name, tc.tax_excluded, fatc.financial_account_id, fatc.currency
FROM accounter_schema.financial_accounts_tax_categories fatc
LEFT JOIN accounter_schema.tax_categories tc
  ON fatc.tax_category_id = tc.id
LEFT JOIN accounter_schema.financial_entities fe
  ON fatc.tax_category_id = fe.id
WHERE fatc.financial_account_id IN $$financialAccountIds;`;
const getTaxCategoryByIDs = sql `
SELECT fe.*, tc.hashavshevet_name, tc.tax_excluded
FROM accounter_schema.tax_categories tc
LEFT JOIN accounter_schema.financial_entities fe
  ON fe.id = tc.id
WHERE tc.id IN $$Ids;`;
const getAllTaxCategories = sql `
SELECT fe.*, tc.hashavshevet_name, tc.tax_excluded
FROM accounter_schema.tax_categories tc
LEFT JOIN accounter_schema.financial_entities fe
  ON fe.id = tc.id;`;
const updateTaxCategory = sql `
UPDATE accounter_schema.tax_categories
SET
hashavshevet_name = COALESCE(
  $hashavshevetName,
  hashavshevet_name
),
tax_excluded = COALESCE(
  $taxExcluded,
  tax_excluded
)
WHERE id = $taxCategoryId
RETURNING *;
`;
const updateBusinessTaxCategory = sql `
UPDATE accounter_schema.business_tax_category_match
SET
tax_category_id = COALESCE(
  $taxCategoryId,
  tax_category_id
)
WHERE
  owner_id = $ownerId
  AND business_id = $businessId
RETURNING *;
`;
const insertTaxCategory = sql `
  INSERT INTO accounter_schema.tax_categories (id, hashavshevet_name, tax_excluded, owner_id)
  VALUES ($id, $hashavshevetName, $taxExcluded, $ownerId)
  RETURNING *;`;
const insertBusinessTaxCategory = sql `
  INSERT INTO accounter_schema.business_tax_category_match (business_id, owner_id, tax_category_id)
  VALUES ($businessId, $ownerId, $taxCategoryId)
  RETURNING *;`;
const deleteBusinessTaxCategory = sql `
  DELETE FROM accounter_schema.business_tax_category_match
  WHERE
    business_id = $businessId
    AND owner_id = $ownerId
  RETURNING *;`;
const deleteTaxCategory = sql `
  DELETE FROM accounter_schema.tax_categories
  WHERE
    id = $taxCategoryId
  RETURNING *;`;
const replaceTaxCategories = sql `
    WITH accounts AS (
      UPDATE accounter_schema.financial_accounts_tax_categories
      SET tax_category_id = $targetTaxCategoryId
      WHERE tax_category_id = $taxCategoryIdToReplace
      RETURNING financial_account_id
    ),
    business_match AS (
      UPDATE accounter_schema.business_tax_category_match
      SET tax_category_id = $targetTaxCategoryId
      WHERE tax_category_id = $taxCategoryIdToReplace
      RETURNING business_id
    )
    UPDATE accounter_schema.charges
      SET tax_category_id = $targetTaxCategoryId
      WHERE tax_category_id = $taxCategoryIdToReplace
    RETURNING id;
  `;
let TaxCategoriesProvider = class TaxCategoriesProvider {
    db;
    adminContextProvider;
    constructor(db, adminContextProvider) {
        this.db = db;
        this.adminContextProvider = adminContextProvider;
    }
    async batchTaxCategoryByBusinessIDs(businessIds) {
        const BusinessIdsSet = new Set(businessIds);
        const taxCategories = await getTaxCategoryByBusinessIDs.run({
            BusinessIds: BusinessIdsSet.size === 0 ? [null] : Array.from(BusinessIdsSet),
        }, this.db);
        return businessIds.map(businessId => taxCategories.find(tc => tc.business_id === businessId));
    }
    taxCategoryByBusinessIDsLoader = new DataLoader((businessIds) => this.batchTaxCategoryByBusinessIDs(businessIds));
    async batchTaxCategoryByIDs(ids) {
        const taxCategories = await getTaxCategoryByIDs.run({
            Ids: ids,
        }, this.db);
        return ids.map(id => taxCategories.find(tc => tc.id === id));
    }
    taxCategoryByIdLoader = new DataLoader((ids) => this.batchTaxCategoryByIDs(ids));
    async batchTaxCategoriesBySortCodes(sortCodes) {
        const taxCategories = await getTaxCategoryBySortCodes.run({
            sortCodes: [...sortCodes],
        }, this.db);
        return sortCodes.map(sortCode => taxCategories.filter(tc => tc.sort_code === sortCode));
    }
    taxCategoriesBySortCodeLoader = new DataLoader((sortCodes) => this.batchTaxCategoriesBySortCodes(sortCodes));
    async batchTaxCategoryByFinancialAccountIdsAndCurrencies(entries) {
        const FinancialAccountIdsSet = new Set(entries.map(e => e.financialAccountId));
        const CurrenciesSet = new Set(entries.map(e => e.currency));
        const taxCategories = await getTaxCategoryByFinancialAccountIdsAndCurrencies.run({
            FinancialAccountIds: FinancialAccountIdsSet.size === 0 ? [null] : Array.from(FinancialAccountIdsSet),
            Currencies: CurrenciesSet.size === 0 ? [null] : Array.from(CurrenciesSet),
        }, this.db);
        return entries.map(({ financialAccountId, currency }) => taxCategories.find(tc => tc.financial_account_id === financialAccountId && tc.currency === currency));
    }
    taxCategoryByFinancialAccountIdsAndCurrenciesLoader = new DataLoader((keys) => this.batchTaxCategoryByFinancialAccountIdsAndCurrencies(keys), {
        cacheKeyFn: key => `${key.financialAccountId}-${key.currency}`,
    });
    async batchTaxCategoryByFinancialAccountOwnerIds(ownerIds) {
        const taxCategories = await getTaxCategoryByFinancialAccountOwnerIds.run({
            ownerIds,
        }, this.db);
        return ownerIds.map(id => taxCategories.filter(tc => tc.financial_account_owner_id === id));
    }
    taxCategoryByFinancialAccountOwnerIdsLoader = new DataLoader((keys) => this.batchTaxCategoryByFinancialAccountOwnerIds(keys));
    async batchTaxCategoryByFinancialAccountIds(financialAccountIds) {
        const taxCategories = await getTaxCategoryByFinancialAccountIds.run({
            financialAccountIds,
        }, this.db);
        return financialAccountIds.map(id => taxCategories.filter(tc => tc.financial_account_id === id));
    }
    taxCategoryByFinancialAccountIdsLoader = new DataLoader((keys) => this.batchTaxCategoryByFinancialAccountIds(keys));
    getAllTaxCategories() {
        return getAllTaxCategories.run(undefined, this.db);
    }
    updateTaxCategory(params) {
        if (params.taxCategoryId)
            this.invalidateTaxCategoryById(params.taxCategoryId);
        return updateTaxCategory.run(params, this.db);
    }
    updateBusinessTaxCategory(params) {
        if (params.taxCategoryId)
            this.invalidateTaxCategoryById(params.taxCategoryId);
        return updateBusinessTaxCategory.run(params, this.db);
    }
    async insertTaxCategory(params) {
        if (!params.id) {
            throw new Error('Missing required parameters');
        }
        const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
        return insertTaxCategory.run(reassureOwnerIdExists(params, ownerId), this.db).catch(error => {
            console.error(`Failed to insert tax category: ${error.message}`);
            throw error;
        });
    }
    insertBusinessTaxCategory(params) {
        return insertBusinessTaxCategory.run(params, this.db);
    }
    deleteBusinessTaxCategory(params) {
        return deleteBusinessTaxCategory.run(params, this.db);
    }
    async deleteTaxCategoryById(taxCategoryId) {
        const entity = await this.taxCategoryByIdLoader.load(taxCategoryId);
        if (!entity) {
            throw new Error(`Tax category with id ${taxCategoryId} not found`);
        }
        this.invalidateTaxCategoryById(taxCategoryId);
        // delete tax category
        deleteTaxCategory.run({ taxCategoryId }, this.db);
    }
    async replaceTaxCategory(targetTaxCategoryId, taxCategoryIdToReplace, deleteTaxCategory = false) {
        const [taxCategoryToReplace, taxCategory] = await Promise.all([
            this.taxCategoryByIdLoader.load(targetTaxCategoryId),
            this.taxCategoryByIdLoader.load(taxCategoryIdToReplace),
        ]);
        if (!taxCategoryToReplace) {
            throw new Error(`Tax category with id ${taxCategoryIdToReplace} not found`);
        }
        if (!taxCategory) {
            throw new Error(`Tax category with id ${targetTaxCategoryId} not found`);
        }
        this.invalidateTaxCategoryById(taxCategoryIdToReplace);
        this.invalidateTaxCategoryById(targetTaxCategoryId);
        await replaceTaxCategories.run({
            targetTaxCategoryId,
            taxCategoryIdToReplace,
        }, this.db);
        if (deleteTaxCategory) {
            await this.deleteTaxCategoryById(taxCategoryIdToReplace);
        }
    }
    invalidateTaxCategoryById(taxCategoryId) {
        this.taxCategoryByBusinessIDsLoader.clearAll();
        this.taxCategoryByFinancialAccountIdsAndCurrenciesLoader.clearAll();
        this.taxCategoryByFinancialAccountIdsLoader.clearAll();
        this.taxCategoryByFinancialAccountOwnerIdsLoader.clearAll();
        this.taxCategoriesBySortCodeLoader.clearAll();
        this.taxCategoryByIdLoader.clear(taxCategoryId);
    }
    clearCache() {
        this.taxCategoryByIdLoader.clearAll();
        this.taxCategoriesBySortCodeLoader.clearAll();
        this.taxCategoryByBusinessIDsLoader.clearAll();
        this.taxCategoryByFinancialAccountIdsAndCurrenciesLoader.clearAll();
        this.taxCategoryByFinancialAccountIdsLoader.clearAll();
        this.taxCategoryByFinancialAccountOwnerIdsLoader.clearAll();
    }
};
TaxCategoriesProvider = __decorate([
    Injectable({
        scope: Scope.Operation,
        global: true,
    }),
    __metadata("design:paramtypes", [TenantAwareDBClient,
        AdminContextProvider])
], TaxCategoriesProvider);
export { TaxCategoriesProvider };
//# sourceMappingURL=tax-categories.provider.js.map