import { CheckDuplicateHelper } from 'src/core/helpers/query/check-duplicate.helpers';
import { BaseManager } from '../base.manager';
import { ValidateRelationHelper } from 'src/core/helpers/validation/validate-relation.helper';
import { columnUniques } from 'src/core/strings/constants/interface.constants';
import { RecordLog } from 'src/modules/configuration/log/domain/entities/log.event';
import { OPERATION } from 'src/core/strings/constants/base.constants';
import { HttpStatus, NotFoundException } from '@nestjs/common';

export abstract class BaseUpdateManager<Entity> extends BaseManager {
  protected dataId: string;
  protected result: Entity;
  protected oldData: Entity;
  protected duplicateColumn: string[];
  abstract get entityTarget(): any;
  abstract get uniqueColumns(): columnUniques[];

  setData(id: string, entity: Entity): void {
    this.dataId = id;
    this.data = entity;
  }

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

    if (!this.oldData) {
      throw new NotFoundException({
        statusCode: HttpStatus.NOT_FOUND,
        message: `Gagal! Data denga id ${this.dataId} tidak ditemukan`,
        error: 'Entity Not Found',
      });
    }

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

    if (this.uniqueColumns.length) {
      await new CheckDuplicateHelper(
        this.dataService,
        this.tableName,
        this.uniqueColumns,
        this.data,
        this.dataId,
      ).execute();
    }
  }

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

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

    this.publishEvents();
  }

  async getResult(): Promise<Entity> {
    return await this.dataService.getOneByOptions({
      where: {
        id: this.dataId,
      },
    });
  }

  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 data ${this.tableName}`,
        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: topic.data?.['id'] ?? this.dataId,
          old: this.oldData,
          data: data ?? topic.data,
          user: this.user,
          description: '',
          module: this.tableName,
          op: OPERATION.UPDATE,
        }),
      ]);
    }
  }
}
