import { BaseManager } from '../base.manager';
import {
  EventTopics,
  columnUniques,
  validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { HttpStatus, UnprocessableEntityException } from '@nestjs/common';
import { SelectQueryBuilder } from 'typeorm';

export abstract class BaseChangePosition<Entity> extends BaseManager {
  protected result: Entity;
  protected duplicateColumn: string[];
  protected startData: Entity;
  protected endData: Entity;
  protected columnSort: string;

  protected firstDataId: number;
  protected lastSort: number;
  protected sortTo: number;

  abstract get entityTarget(): any;

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

  async beforeProcess(): Promise<void> {
    if (!this.data?.end || this.data.start == this.data?.end) {
      throw new UnprocessableEntityException({
        statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
        message: 'Gagal! tolong pindahkan ke posisi lain',
        error: 'Unprocessable Entity',
      });
    }

    this.startData = await this.dataService.getOneByOptions({
      where: {
        id: this.data.start,
      },
    });

    if (!this.startData) {
      throw new UnprocessableEntityException({
        statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
        message: `Gagal! data dengan id : ${this.data.start} tidak ditemukan`,
        error: 'Unprocessable Entity',
      });
    }

    this.endData = await this.dataService.getOneByOptions({
      where: {
        id: this.data.end,
      },
    });

    if (!this.endData) {
      throw new UnprocessableEntityException({
        statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
        message: `Gagal! data dengan id : ${this.data.end} tidak ditemukan`,
        error: 'Unprocessable Entity',
      });
    }

    if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
      // drag from up
      this.firstDataId = this.startData[this.columnSort];
      this.lastSort = this.endData[this.columnSort];
      this.sortTo = this.lastSort;
    } else if (
      this.endData[this.columnSort] < this.startData[this.columnSort]
    ) {
      // drag from bottom
      this.firstDataId = this.endData[this.columnSort];
      this.lastSort = this.startData[this.columnSort];
      this.sortTo = this.firstDataId;
    }
  }

  async prepareData(): Promise<void> {
    Object.assign(this.data, {
      creator_id: this.user.id,
      creator_name: this.user.name,
      created_at: new Date().getTime(),
      updated_at: new Date().getTime(),
    });
  }

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

  async process(): Promise<void> {
    let dataArrange: Entity[];

    const queryBuilder = this.dataService
      .getRepository()
      .createQueryBuilder(this.tableName)
      .where(`${this.tableName}.${this.columnSort} between :data1 and :data2`, {
        data1: this.firstDataId,
        data2: this.lastSort,
      });

    const datas = await queryBuilder
      .orderBy(`${this.tableName}.${this.columnSort}`, 'ASC')
      .getManyAndCount();

    if (datas[0].length) {
      let dataFirst = datas[0][0][this.columnSort];
      const data = datas[0];
      const length = datas[1];

      if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
        // drag from above
        const dataDragged = data[0];
        const arraySlice = data.slice(1, length);
        dataArrange = arraySlice.concat([dataDragged]);
      } else if (
        this.endData[this.columnSort] < this.startData[this.columnSort]
      ) {
        // drag from bottom
        const dataDragged = data[length - 1];
        const arraySlice = data.slice(0, length - 1);

        dataArrange = [dataDragged].concat(arraySlice);
      }

      for (let i = 0; i < length; i++) {
        dataArrange[i][this.columnSort] = dataFirst;
        dataFirst++;
      }

      await this.dataService.createMany(
        this.queryRunner,
        this.entityTarget,
        dataArrange,
      );
    }
  }

  get validateRelations(): validateRelations[] {
    return [];
  }

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

  getResult(): string {
    return `Success! Data ${this.startData['name']} successfully moved to ${this.sortTo}`;
  }
}
