import { pick } from 'lodash';
import { Knex } from 'knex';
import { InventoryTransaction } from '../models/InventoryTransaction';

export class InventoryAverageCostMethod {
  constructor() {}

  /**
   * Computes the cost of the given rate and quantity.
   * @param {number} rate - The given rate.
   * @param {number} quantity - The given quantity.
   * @returns {number}
   */
  private getCost(rate: number, quantity: number) {
    return quantity ? rate * quantity : rate;
  }

  /**
   * Records the journal entries from specific item inventory transactions.
   * @param {IInventoryTransaction[]} invTransactions
   * @param {number} openingAverageCost
   * @param {string} referenceType
   * @param {number} referenceId
   * @param {JournalCommand} journalCommands
   */
  public trackingCostTransactions(
    invTransactions: InventoryTransaction[],
    openingQuantity: number = 0,
    openingCost: number = 0,
  ) {
    const costTransactions: any[] = [];

    // Cumulative item quantity and cost. This will decrement after
    // each out transactions depends on its quantity and cost.
    let accQuantity: number = openingQuantity;
    let accCost: number = openingCost;

    invTransactions.forEach((invTransaction: InventoryTransaction) => {
      const commonEntry = {
        invTransId: invTransaction.id,
        ...pick(invTransaction, [
          'date',
          'direction',
          'itemId',
          'quantity',
          'rate',
          'entryId',
          'transactionId',
          'transactionType',
          'createdAt',
          'costAccountId',
          'branchId',
          'warehouseId',
        ]),
        inventoryTransactionId: invTransaction.id,
      };
      switch (invTransaction.direction) {
        case 'IN':
          const inCost = this.getCost(
            invTransaction.rate,
            invTransaction.quantity,
          );
          // Increases the quantity and cost in `IN` inventory transactions.
          accQuantity += invTransaction.quantity;
          accCost += inCost;

          costTransactions.push({
            ...commonEntry,
            cost: inCost,
          });
          break;
        case 'OUT':
          // Average cost = Total cost / Total quantity
          const averageCost = accQuantity ? accCost / accQuantity : 0;

          const quantity =
            accQuantity > 0
              ? Math.min(invTransaction.quantity, accQuantity)
              : invTransaction.quantity;

          // Cost = the transaction quantity * Average cost.
          const cost = this.getCost(averageCost, quantity);

          // Revenue = transaction quanity * rate.
          // const revenue = quantity * invTransaction.rate;
          costTransactions.push({
            ...commonEntry,
            quantity,
            cost,
          });
          accQuantity = Math.max(accQuantity - quantity, 0);
          accCost = Math.max(accCost - cost, 0);

          if (invTransaction.quantity > quantity) {
            const remainingQuantity = Math.max(
              invTransaction.quantity - quantity,
              0,
            );
            const remainingIncome = remainingQuantity * invTransaction.rate;

            costTransactions.push({
              ...commonEntry,
              quantity: remainingQuantity,
              cost: 0,
            });
            accQuantity = Math.max(accQuantity - remainingQuantity, 0);
            accCost = Math.max(accCost - remainingIncome, 0);
          }
          break;
      }
    });
    return costTransactions;
  }
}
