import { ValidateRelationHelper } from 'src/core/helpers/validation/validate-relation.helper';
import { BaseManager } from '../base.manager';
import {
  OPERATION,
  QUEUE_STATUS,
  STATUS,
} from 'src/core/strings/constants/base.constants';
import * as _ from 'lodash';
import { RecordLog } from 'src/modules/configuration/log/domain/entities/log.event';

export abstract class BaseUpdateStatusManager<Entity> extends BaseManager {
  protected dataId: string;
  protected result: Entity;
  protected oldData: Entity;
  protected dataStatus: STATUS | QUEUE_STATUS;
  protected relations = [];
  protected duplicateColumn: string[];
  abstract get entityTarget(): any;

  setData(id: string, status: STATUS | QUEUE_STATUS): void {
    /**
     * // TODO: Handle case confirm multiple tabs;
     * Pola id yang dikirim dirubah menjadi data_id___updated_at
     * Untuk mendapatkan value id nya saja dan menghindari breaking change
     * karena sudah digunakan sebelumnya lakukan split dari data ids yang dikirim
     * Example:
     * this.dataId = id.split('___')[0]
     *
     * Simpan data id yang mempunyai update_at kedalam valiable baru
     * Example:
     * this.dataIdsWithDate = {
     *      id: id.split('___')[0],
     *      updated_at: id.split('___')[1]
     *  }
     */

    this.dataId = id;
    this.dataStatus = status;
  }

  async prepareData(): Promise<void> {
    this.data = await this.dataService.getOneByOptions({
      where: {
        id: this.dataId,
      },
      relations: this.relations,
    });
    this.oldData = _.cloneDeep(this.data);

    Object.assign(this.data, {
      editor_id: this.user.id,
      editor_name: this.user.name,
      updated_at: new Date().getTime(),
      status: this.dataStatus,
    });
  }

  async process(): Promise<void> {
    await new ValidateRelationHelper(
      this.dataId,
      this.dataService,
      this.validateRelations,
      this.tableName,
    ).execute();

    /**
     * // TODO: Handle case confirm multiple tabs;
     * IF =>  updated_at sama dengen data yang di database
     * THEN =>
     *        - Lakukan update data dengan where condition id dan updated_at
     *        - EXPECTATION = > status akan berubah jika updated_at yang dikirim dari FE sama dengen yang di database
     * IF => updated_at beda maka retun curent data tanpa harus malakukan update status dan publish event
     * IF => FE tidak menambahkan updated_at makan lakukan update dan publishEvent
     */

    this.result = await this.dataService.update(
      this.queryRunner,
      this.entityTarget,
      { id: this.dataId },
      this.data,
    );

    this.publishEvents();
  }

  abstract getResult(): string;

  async publishEvents() {
    this.eventBus.publish(
      new RecordLog({
        id: this.result['id'],
        old: this.oldData,
        data: this.result,
        user: this.user,
        description: `${this.user.name} update status data ${this.tableName} to ${this.dataStatus}`,
        module: this.tableName,
        op: OPERATION.UPDATE,
      }),
    );

    if (!this.eventTopics.length) return;
    for (const topic of this.eventTopics) {
      let data;
      if (!topic.data) {
        data = await this.dataService.getOneByOptions({
          where: {
            id: this.dataId,
          },
          relations: topic.relations,
        });
      }

      this.eventBus.publishAll([
        new topic.topic({
          id: data?.['id'] ?? topic?.data?.['id'],
          old: this.oldData,
          data: data ?? topic.data,
          user: this.user,
          description: '',
          module: this.tableName,
          op: OPERATION.UPDATE,
        }),
      ]);
    }
  }
}
