import { Model, raw } from 'objection';
import { castArray } from 'lodash';
import * as moment from 'moment';
import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils';
import { TInventoryTransactionDirection } from '../types/InventoryCost.types';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
import { InventoryTransactionMeta } from './InventoryTransactionMeta';

export class InventoryTransaction extends TenantBaseModel {
  date!: Date | string;
  direction!: TInventoryTransactionDirection;
  itemId!: number;
  quantity!: number | null;
  rate!: number;
  transactionType!: string;
  transactionId!: number;
  costAccountId?: number;
  entryId!: number;

  createdAt?: Date;
  updatedAt?: Date;

  warehouseId?: number;
  meta?: InventoryTransactionMeta;

  /**
   * Table name
   */
  static get tableName() {
    return 'inventory_transactions';
  }

  /**
   * Model timestamps.
   */
  static get timestamps() {
    return ['createdAt', 'updatedAt'];
  }

  /**
   * Retrieve formatted reference type.
   * @return {string}
   */
  get transcationTypeFormatted(): string {
    return getTransactionTypeLabel(this.transactionType);
  }

  /**
   * Model modifiers.
   */
  static get modifiers() {
    return {
      filterDateRange(
        query,
        startDate,
        endDate,
        type: moment.unitOfTime.StartOf = 'day',
      ) {
        const dateFormat = 'YYYY-MM-DD';
        const fromDate = moment(startDate).startOf(type).format(dateFormat);
        const toDate = moment(endDate).endOf(type).format(dateFormat);

        if (startDate) {
          query.where('date', '>=', fromDate);
        }
        if (endDate) {
          query.where('date', '<=', toDate);
        }
      },

      itemsTotals(builder) {
        builder.select('itemId');
        builder.sum('rate as rate');
        builder.sum('quantity as quantity');
        builder.select(raw('SUM(`QUANTITY` * `RATE`) as COST'));
        builder.groupBy('itemId');
      },

      INDirection(builder) {
        builder.where('direction', 'IN');
      },

      OUTDirection(builder) {
        builder.where('direction', 'OUT');
      },

      /**
       * Filters transactions by the given branches.
       */
      filterByBranches(query, branchesIds) {
        const formattedBranchesIds = castArray(branchesIds);

        query.whereIn('branch_id', formattedBranchesIds);
      },

      /**
       * Filters transactions by the given warehosues.
       */
      filterByWarehouses(query, warehousesIds) {
        const formattedWarehousesIds = castArray(warehousesIds);

        query.whereIn('warehouse_id', formattedWarehousesIds);
      },
    };
  }

  /**
   * Relationship mapping.
   */
  static get relationMappings() {
    const { Item } = require('../../Items/models/Item');
    const {
      ItemEntry,
    } = require('../../TransactionItemEntry/models/ItemEntry');
    const { InventoryTransactionMeta } = require('./InventoryTransactionMeta');
    const { InventoryCostLotTracker } = require('./InventoryCostLotTracker');

    return {
      // Transaction meta.
      meta: {
        relation: Model.HasOneRelation,
        modelClass: InventoryTransactionMeta,
        join: {
          from: 'inventory_transactions.id',
          to: 'inventory_transaction_meta.inventoryTransactionId',
        },
      },
      // Item cost aggregated.
      itemCostAggregated: {
        relation: Model.HasOneRelation,
        modelClass: InventoryCostLotTracker,
        join: {
          from: 'inventory_transactions.itemId',
          to: 'inventory_cost_lot_tracker.itemId',
        },
        filter(query) {
          query.select('itemId');
          query.sum('cost as cost');
          query.sum('quantity as quantity');
          query.groupBy('itemId');
        },
      },
      costLotAggregated: {
        relation: Model.HasOneRelation,
        modelClass: InventoryCostLotTracker,
        join: {
          from: 'inventory_transactions.id',
          to: 'inventory_cost_lot_tracker.inventoryTransactionId',
        },
        filter(query) {
          query.sum('cost as cost');
          query.sum('quantity as quantity');
          query.groupBy('inventoryTransactionId');
        },
      },
      item: {
        relation: Model.BelongsToOneRelation,
        modelClass: Item,
        join: {
          from: 'inventory_transactions.itemId',
          to: 'items.id',
        },
      },
      itemEntry: {
        relation: Model.BelongsToOneRelation,
        modelClass: ItemEntry,
        join: {
          from: 'inventory_transactions.entryId',
          to: 'items_entries.id',
        },
      },
    };
  }
}
