import { Injectable } from '@nestjs/common';
import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager';
import { EventTopics } from 'src/core/strings/constants/interface.constants';
import { TransactionType } from 'src/modules/transaction/transaction/constants';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity';
import { Brackets, DeleteQueryBuilder, IsNull, MoreThan, Not } from 'typeorm';
import * as _ from 'lodash';
import { EMPTY_UUID, STATUS } from 'src/core/strings/constants/base.constants';
import { RecapReconciliationDto } from '../../../infrastructure/dto/recap.dto';

@Injectable()
export class RecapReconciliationManager extends BaseCustomManager<TransactionEntity> {
  private recapTransactions = {};
  private bodyFilter: any;
  private paymentDate: string;

  setBodyFilter(body: RecapReconciliationDto) {
    this.bodyFilter = body;
    this.paymentDate = body.payment_date;
  }

  get entityTarget(): any {
    return TransactionModel;
  }

  getResult() {
    return 'Berhasil recap data transaksi';
  }

  get eventTopics(): EventTopics[] {
    return [];
  }

  async validateProcess(): Promise<void> {
    return;
  }

  async beforeProcess(): Promise<void> {
    const transactions = await this.dataService.getManyByOptions({
      where: {
        is_recap_transaction: false,
        payment_type: TransactionType.COUNTER,
        status: STATUS.SETTLED,
        payment_date: this.paymentDate,
        payment_type_counter: Not(IsNull()),
        payment_total: MoreThan(0),
      },
    });

    for (const transaction of transactions) {
      const {
        creator_counter_no,
        payment_type_counter,
        payment_type_method_id,
        payment_date,
      } = transaction;
      const group_by =
        creator_counter_no +
        '|' +
        payment_type_counter +
        '|' +
        payment_type_method_id +
        '|' +
        payment_date;

      if (!this.recapTransactions[group_by]) {
        this.recapTransactions[group_by] = [];
      }
      this.recapTransactions[group_by].push(transaction);
    }

    return;
  }

  async process(): Promise<void> {
    const total_recap = Object.keys(this.recapTransactions);

    const deleteQuery: DeleteQueryBuilder<TransactionEntity> = this.dataService
      .getRepository()
      .createQueryBuilder(this.tableName)
      .delete();
    deleteQuery.where(
      `payment_date = :payment_date and is_recap_transaction = true`,
      {
        payment_date: this.paymentDate,
      },
    );
    deleteQuery.andWhere(
      new Brackets((qb) => {
        for (const condition of total_recap) {
          const [
            creator_counter_no,
            payment_type_counter,
            payment_type_method_id,
          ] = condition.split('|');

          const whereMethodId =
            payment_type_method_id == 'null'
              ? 'IS NULL'
              : `= '${payment_type_method_id}'`;
          qb.andWhere(
            `NOT (creator_counter_no = '${creator_counter_no}' AND payment_type_counter = '${payment_type_counter}' AND payment_type_method_id ${whereMethodId})`,
          );
        }
      }),
    );

    await deleteQuery.execute();

    for (const recap of total_recap) {
      const first_transaction = this.recapTransactions[recap][0];

      const {
        creator_counter_no,
        payment_type_counter,
        payment_type_method_id,
        payment_date,
      } = first_transaction;

      const query = {
        is_recap_transaction: true,
        payment_date: payment_date,
        creator_counter_no: creator_counter_no,
        payment_type: payment_type_counter,
      };

      if (payment_type_counter != 'cash')
        query['payment_type_method_id'] = payment_type_method_id ?? EMPTY_UUID;

      const exist = await this.dataService.getOneByOptions({
        where: query,
      });

      const new_recap = new TransactionModel();
      const total = _.sumBy(this.recapTransactions[recap], (recap) =>
        parseFloat(recap.payment_total),
      );

      if (exist) {
        if (parseFloat(exist.payment_total) !== total) {
          const newTotal = total;
          const newTotalNet =
            total - parseFloat(exist.reconciliation_mdr ?? '0');

          Object.assign(exist, {
            payment_total: newTotal,
            payment_total_net_profit: newTotalNet,

            //RESET STATUS TO PENDING
            reconciliation_confirm_by: null,
            reconciliation_confirm_date: null,
            reconciliation_status: STATUS.PENDING,
            payment_code: null,
            settlement_date: null,
          });
        } else {
          Object.assign(exist, {
            payment_total: total,
            payment_total_net_profit: total,
          });
        }

        Object.assign(exist, {
          editor_id: this.user.id,
          editor_name: this.user.name,
          updated_at: new Date().getTime(),
        });
      } else {
        Object.assign(new_recap, {
          is_recap_transaction: true,
          payment_total: total,
          payment_total_net_profit: total,
          reconciliation_status: STATUS.PENDING,
          status: STATUS.SETTLED,
          type: TransactionType.COUNTER,
          booking_date: payment_date,
          payment_date: payment_date,
          creator_counter_no: first_transaction.creator_counter_no,
          creator_counter_name: first_transaction.creator_counter_name,
          payment_type: first_transaction.payment_type_counter,
          payment_type_counter: first_transaction.payment_type_counter,
          payment_type_method_id: first_transaction.payment_type_method_id,
          payment_type_method_number:
            first_transaction.payment_type_method_number,
          payment_type_method_name: first_transaction.payment_type_method_name,
          payment_type_method_qr: first_transaction.payment_type_method_qr,
          creator_id: this.user.id,
          creator_name: this.user.name,
          created_at: new Date().getTime(),
          updated_at: new Date().getTime(),
        });
      }

      await this.dataService.create(
        this.queryRunner,
        TransactionModel,
        exist ?? new_recap,
      );
    }
    return;
  }

  async afterProcess(): Promise<void> {
    return;
  }
}
