import type { Injector } from 'graphql-modules';
import type { LedgerProto } from '../../../shared/types/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import type { AdminContext } from '../../admin-context/types.js';
import {
  getChargeBusinesses,
  getChargeTaxCategoryId,
} from '../../charges/helpers/common.helper.js';
import type { IGetChargesByIdsResult } from '../../charges/types.js';
import { ExchangeProvider } from '../../exchange-rates/providers/exchange.provider.js';
import type { IGetTransactionsByChargeIdsResult } from '../../transactions/types.js';
import {
  getFinancialAccountTaxCategoryId,
  LedgerError,
  validateTransactionBasicVariables,
} from './utils.helper.js';

export function splitFeeTransactions(transactions: Array<IGetTransactionsByChargeIdsResult>) {
  const feeTransactions = [];
  const mainTransactions = [];
  for (const transaction of transactions) {
    if (transaction.is_fee) {
      feeTransactions.push(transaction);
    } else {
      mainTransactions.push(transaction);
    }
  }
  return { feeTransactions, mainTransactions };
}

export function isSupplementalFeeTransaction(
  transaction: IGetTransactionsByChargeIdsResult,
  financialAccounts: AdminContext['financialAccounts'],
): boolean {
  const { internalWalletsIds, swiftBusinessId } = financialAccounts;
  if (!transaction.is_fee) {
    return false;
  }
  if (!transaction.business_id) {
    throw new LedgerError(
      `Transaction reference "${transaction.source_reference}" is missing business_id, which is required to figure if fee is supplemental`,
    );
  }

  if (internalWalletsIds.includes(transaction.business_id)) {
    return true;
  }

  const fundamentalFeeBusinesses = [swiftBusinessId].filter(Boolean) as string[];
  if (fundamentalFeeBusinesses.includes(transaction.business_id)) {
    return false;
  }
  throw new LedgerError(
    `Unable to determine if business ID="${transaction.business_id}" is supplemental or fundamental fee`,
  );
}

export async function getEntriesFromFeeTransaction(
  transaction: IGetTransactionsByChargeIdsResult,
  charge: IGetChargesByIdsResult,
  injector: Injector,
): Promise<Array<LedgerProto>> {
  const ledgerEntries: Array<LedgerProto> = [];

  if (!transaction.is_fee) {
    throw new LedgerError(
      `Seems like did a non-fee transaction marked as fee (Transaction ID="${transaction.id}")`,
    );
  }

  if (Number(transaction.amount) === 0) {
    // no ledger entries for 0 amount fee transactions
    return ledgerEntries;
  }

  const {
    defaultLocalCurrency,
    general: {
      taxCategories: { feeTaxCategoryId, generalFeeTaxCategoryId },
    },
    financialAccounts,
  } = await injector.get(AdminContextProvider).getVerifiedAdminContext();

  const isSupplementalFee = isSupplementalFeeTransaction(transaction, financialAccounts);
  const { currency, valueDate, transactionBusinessId } =
    validateTransactionBasicVariables(transaction);

  let amount = Number(transaction.amount);
  let foreignAmount: number | undefined = undefined;

  if (currency !== defaultLocalCurrency) {
    // get exchange rate for currency
    const exchangeRate = await injector
      .get(ExchangeProvider)
      .getExchangeRates(currency, defaultLocalCurrency, valueDate);

    foreignAmount = amount;
    // calculate amounts in ILS
    amount = exchangeRate * amount;
  }

  const isCreditorCounterparty = amount > 0;

  let mainAccount = transactionBusinessId;

  const partialLedgerEntry: LedgerProto = {
    id: transaction.id,
    invoiceDate: transaction.event_date,
    valueDate,
    currency,
    creditAmount1: foreignAmount ? Math.abs(foreignAmount) : undefined,
    localCurrencyCreditAmount1: Math.abs(amount),
    debitAmount1: foreignAmount ? Math.abs(foreignAmount) : undefined,
    localCurrencyDebitAmount1: Math.abs(amount),
    description: transaction.source_description ?? undefined,
    reference: transaction.origin_key,
    isCreditorCounterparty: isSupplementalFee ? isCreditorCounterparty : !isCreditorCounterparty,
    ownerId: charge.owner_id,
    currencyRate: transaction.currency_rate ? Number(transaction.currency_rate) : undefined,
    chargeId: transaction.charge_id,
  };

  if (isSupplementalFee) {
    mainAccount = await getFinancialAccountTaxCategoryId(injector, transaction);
  } else {
    const { mainBusinessId } = await getChargeBusinesses(charge.id, injector);
    const mainBusiness = mainBusinessId ?? undefined;

    const ledgerEntry: LedgerProto = {
      ...partialLedgerEntry,
      creditAccountID1: isCreditorCounterparty ? mainAccount : mainBusiness,
      debitAccountID1: isCreditorCounterparty ? mainBusiness : mainAccount,
    };

    ledgerEntries.push(ledgerEntry);
  }

  const taxCategoryId = await getChargeTaxCategoryId(charge.id, injector);

  const feeTaxCategory =
    taxCategoryId === generalFeeTaxCategoryId ? generalFeeTaxCategoryId : feeTaxCategoryId;

  const ledgerEntry: LedgerProto = {
    ...partialLedgerEntry,
    creditAccountID1: isCreditorCounterparty ? feeTaxCategory : mainAccount,
    debitAccountID1: isCreditorCounterparty ? mainAccount : feeTaxCategory,
  };

  ledgerEntries.push(ledgerEntry);

  return ledgerEntries;
}
