// @ts-nocheck
import * as R from 'ramda';
import { sumBy, isEmpty } from 'lodash';
import {
  IBalanceSheetAccountNode,
  IBalanceSheetCommonNode,
  IBalanceSheetDataNode,
  IBalanceSheetTotal,
} from './BalanceSheet.types';
import { FinancialPreviousYear } from '../../common/FinancialPreviousYear';
import { GConstructor } from '@/common/types/Constructor';
import { FinancialSheet } from '../../common/FinancialSheet';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetRepository } from './BalanceSheetRepository';

export const BalanceSheetComparsionPreviousYear = <
  T extends GConstructor<FinancialSheet>,
>(
  Base: T,
) =>
  class BalanceSheetComparsionPreviousYear extends R.pipe(
    FinancialPreviousYear,
  )(Base) {
    query: BalanceSheetQuery;
    repository: BalanceSheetRepository;

    // ------------------------------
    // # Account
    // ------------------------------
    /**
     * Associates the previous year to account node.
     * @param   {IBalanceSheetDataNode} node
     * @returns {IBalanceSheetDataNode}
     */
    protected assocPreviousYearAccountNode = (
      node: IBalanceSheetDataNode,
    ): IBalanceSheetDataNode => {
      const closingBalance =
        this.repository.PYTotalAccountsLedger.whereAccountId(
          node.id,
        ).getClosingBalance();

      return R.assoc('previousYear', this.getAmountMeta(closingBalance), node);
    };

    /**
     * Assoc previous year attributes to account node.
     * @param {IBalanceSheetAccountNode} node
     * @returns {IBalanceSheetAccountNode}
     */
    protected previousYearAccountNodeComposer = (
      node: IBalanceSheetAccountNode,
    ): IBalanceSheetAccountNode => {
      return R.compose(
        R.when(
          this.isNodeHasHorizontalTotals,
          this.assocPreviousYearAccountHorizNodeComposer,
        ),
        R.when(
          this.query.isPreviousYearPercentageActive,
          this.assocPreviousYearPercentageNode,
        ),
        R.when(
          this.query.isPreviousYearChangeActive,
          this.assocPreviousYearChangetNode,
        ),
        this.assocPreviousYearAccountNode,
      )(node);
    };

    // ------------------------------
    // # Aggregate
    // ------------------------------
    /**
     * Assoc previous year on aggregate node.
     * @param {IBalanceSheetAccountNode} node
     * @returns {IBalanceSheetAccountNode}
     */
    protected assocPreviousYearAggregateNode = (
      node: IBalanceSheetAccountNode,
    ): IBalanceSheetAccountNode => {
      const total = sumBy(node.children, 'previousYear.amount');

      return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
    };

    /**
     * Assoc previous year attributes to aggregate node.
     * @param {IBalanceSheetAccountNode} node
     * @returns {IBalanceSheetAccountNode}
     */
    protected previousYearAggregateNodeComposer = (
      node: IBalanceSheetAccountNode,
    ): IBalanceSheetAccountNode => {
      return R.compose(
        R.when(
          this.query.isPreviousYearPercentageActive,
          this.assocPreviousYearTotalPercentageNode,
        ),
        R.when(
          this.query.isPreviousYearChangeActive,
          this.assocPreviousYearTotalChangeNode,
        ),
        R.when(
          this.isNodeHasHorizontalTotals,
          this.assocPreviousYearAggregateHorizNode,
        ),
        this.assocPreviousYearAggregateNode,
      )(node);
    };

    // ------------------------------
    // # Horizontal Nodes - Aggregate
    // ------------------------------
    /**
     * Assoc previous year total to horizontal node.
     * @param node
     * @returns
     */
    private assocPreviousYearAggregateHorizTotalNode = R.curry(
      (node, index, totalNode) => {
        const total = this.getPYHorizNodesTotalSumation(index, node);

        return R.assoc(
          'previousYear',
          this.getTotalAmountMeta(total),
          totalNode,
        );
      },
    );

    /**
     * Compose previous year to aggregate horizontal nodes.
     * @param   {IBalanceSheetTotal} node
     * @returns {IBalanceSheetTotal}
     */
    private previousYearAggregateHorizNodeComposer = R.curry(
      (
        node: IBalanceSheetCommonNode,
        horiontalTotalNode: IBalanceSheetTotal,
        index: number,
      ): IBalanceSheetTotal => {
        return R.compose(
          R.when(
            this.query.isPreviousYearPercentageActive,
            this.assocPreviousYearTotalPercentageNode,
          ),
          R.when(
            this.query.isPreviousYearChangeActive,
            this.assocPreviousYearTotalChangeNode,
          ),
          R.when(
            this.query.isPreviousYearActive,
            this.assocPreviousYearAggregateHorizTotalNode(node, index),
          ),
          R.when(
            this.query.isPreviousYearActive,
            this.assocPreviousYearHorizNodeFromToDates,
          ),
        )(horiontalTotalNode);
      },
    );

    /**
     * Assoc
     * @param   {IBalanceSheetCommonNode} node
     * @returns {IBalanceSheetCommonNode}
     */
    public assocPreviousYearAggregateHorizNode = (
      node: IBalanceSheetCommonNode,
    ): IBalanceSheetCommonNode => {
      const horizontalTotals = R.addIndex(R.map)(
        this.previousYearAggregateHorizNodeComposer(node),
        node.horizontalTotals,
      ) as IBalanceSheetTotal[];

      return R.assoc('horizontalTotals', horizontalTotals, node);
    };

    // ------------------------------
    // # Horizontal Nodes - Account.
    // ------------------------------
    /**
     * Retrieve the given account total in the given period.
     * @param   {number} accountId - Account id.
     * @param   {Date} fromDate - From date.
     * @param   {Date} toDate - To date.
     * @returns {number}
     */
    private getAccountPYDatePeriodTotal = R.curry(
      (accountId: number, fromDate: Date, toDate: Date): number => {
        const PYPeriodsTotal =
          this.repository.PYPeriodsAccountsLedger.whereAccountId(accountId)
            .whereToDate(toDate)
            .getClosingBalance();

        const PYPeriodsOpeningTotal =
          this.repository.PYPeriodsOpeningAccountLedger.whereAccountId(
            accountId,
          ).getClosingBalance();

        return PYPeriodsOpeningTotal + PYPeriodsTotal;
      },
    );

    /**
     * Assoc preivous year to account horizontal total node.
     * @param   {IBalanceSheetAccountNode} node
     * @returns {}
     */
    private assocPreviousYearAccountHorizTotal = R.curry(
      (node: IBalanceSheetAccountNode, totalNode) => {
        const total = this.getAccountPYDatePeriodTotal(
          node.id,
          totalNode.previousYearFromDate.date,
          totalNode.previousYearToDate.date,
        );
        return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
      },
    );

    /**
     * Previous year account horizontal node composer.
     * @param   {IBalanceSheetAccountNode} node -
     * @param   {IBalanceSheetTotal}
     * @returns {IBalanceSheetTotal}
     */
    private previousYearAccountHorizNodeCompose = R.curry(
      (
        node: IBalanceSheetAccountNode,
        horizontalTotalNode: IBalanceSheetTotal,
      ): IBalanceSheetTotal => {
        return R.compose(
          R.when(
            this.query.isPreviousYearPercentageActive,
            this.assocPreviousYearPercentageNode,
          ),
          R.when(
            this.query.isPreviousYearChangeActive,
            this.assocPreviousYearChangetNode,
          ),
          R.when(
            this.query.isPreviousYearActive,
            this.assocPreviousYearAccountHorizTotal(node),
          ),
          R.when(
            this.query.isPreviousYearActive,
            this.assocPreviousYearHorizNodeFromToDates,
          ),
        )(horizontalTotalNode);
      },
    );

    /**
     * Assoc previous year horizontal nodes to account node.
     * @param   {IBalanceSheetAccountNode} node
     * @returns {IBalanceSheetAccountNode}
     */
    private assocPreviousYearAccountHorizNodeComposer = (
      node: IBalanceSheetAccountNode,
    ) => {
      const horizontalTotals = R.map(
        this.previousYearAccountHorizNodeCompose(node),
        node.horizontalTotals,
      );
      return R.assoc('horizontalTotals', horizontalTotals, node);
    };

    // ------------------------------
    // # Horizontal Nodes - Aggregate.
    // ------------------------------
    /**
     * Detarmines whether the given node has horizontal totals.
     * @param   {IBalanceSheetCommonNode} node
     * @returns {boolean}
     */
    public isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
      !isEmpty(node.horizontalTotals);
  };
