import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { BaseDataService } from 'src/core/modules/data/service/base-data.service';
import { validateRelations } from 'src/core/strings/constants/interface.constants';

@Injectable()
export class ValidateRelationHelper<Entity> {
  constructor(
    private dataId: string,
    private dataService: BaseDataService<Entity>,
    private relations: validateRelations[],
    private tableName: string,
  ) {}

  async execute() {
    const repository = this.dataService.getRepository();
    const queryBuilder = repository.createQueryBuilder(this.tableName);

    // load relation
    for (const relation of this.relations) {
      if (relation.singleQuery) {
        queryBuilder.leftJoinAndMapOne(
          `${this.tableName}.${relation.relation}`,
          `${this.tableName}.${relation.relation}`,
          relation.relation,
        );
      } else if (relation.query) {
        queryBuilder.loadRelationCountAndMap(
          `${this.tableName}.total_${relation.relation}`,
          `${this.tableName}.${relation.relation}`,
          `total_${relation.relation}`,
          relation.query,
        );
      } else {
        queryBuilder.loadRelationCountAndMap(
          `${this.tableName}.total_${relation.relation}`,
          `${this.tableName}.${relation.relation}`,
          `total_${relation.relation}`,
        );
      }
    }

    // filtering data only with specific data
    queryBuilder.where(`${this.tableName}.id in ('${this.dataId}')`);

    // get data
    const data = await queryBuilder.getOne();

    // process validasi
    for (const relation of this.relations) {
      const message =
        relation.message ??
        `Failed! this data already connected to ${relation.relation}`;

      if (relation.singleQuery) {
        const relationObject = data[relation.relation] ?? {};
        const isRelationExist = `${relation.singleQuery[0]}` in relationObject;
        const relationColumn =
          data[relation.relation]?.[`${relation.singleQuery[0]}`];
        if (
          isRelationExist &&
          this.mappingValidator(
            relationColumn,
            relation.singleQuery[1],
            relation.singleQuery[2],
          )
        )
          throw new UnprocessableEntityException(message);
      } else if (data[`total_${relation.relation}`] > 0)
        throw new UnprocessableEntityException(message);
    }
  }

  mappingValidator(column, operator, value) {
    const candidates = Array.isArray(value) ? value : [value];
    switch (operator) {
      case '!=':
        return column != value;

      case '==':
        return column == value;

      case 'in':
        return candidates.includes(column);

      case 'not in':
        return !candidates.includes(column);

      default:
        return column == value;
    }
  }
}
