import { Filter } from "@codelytv/criteria";
import { FindOptions, Op, OrderItem, WhereOptions } from "sequelize";
import { Criteria } from "./critera";

type Mappings = { [key: string]: string };

export class CriteriaToSequelizeConverter {
  //convert(fieldsToSelect: string[], criteria: Criteria, mappings: Mappings = {}): FindOptions {
  convert(criteria: Criteria, mappings: Mappings = {}): FindOptions {
    const options: FindOptions = {};

    // Selección de campos
    /*if (fieldsToSelect.length > 0) {
      options.attributes = fieldsToSelect;
    }*/

    // Filtros
    if (criteria.hasFilters()) {
      options.where = this.buildWhere(criteria.filters.value, mappings);
    }

    // Orden
    if (criteria.hasOrder()) {
      const field = mappings[criteria.order.orderBy.value] || criteria.order.orderBy.value;
      const direction = criteria.order.orderType.value.toUpperCase();
      options.order = [[field, direction]] as OrderItem[];
    }

    // Paginación
    if (criteria.pageSize !== null) {
      options.limit = criteria.pageSize;
    }

    if (criteria.pageSize !== null && criteria.pageNumber !== null) {
      options.offset = criteria.pageSize * criteria.pageNumber;
    }

    return options;
  }

  private buildWhere(filters: Filter[], mappings: Mappings): WhereOptions {
    const where: WhereOptions = {};

    filters.forEach((filter) => {
      const field = mappings[filter.field.value] || filter.field.value;
      const operator = this.mapOperator(filter.operator.value);
      const value = filter.value.value;

      if (!where[field]) {
        where[field] = {};
      }

      where[field][operator] = this.transformValue(operator, value);
    });

    return where;
  }

  private mapOperator(operator: string): symbol {
    switch (operator) {
      case "CONTAINS":
        return Op.like;
      case "NOT_CONTAINS":
        return Op.notLike;
      case "NOT_EQUALS":
        return Op.ne;
      case "GREATER_THAN":
        return Op.gt;
      case "GREATER_THAN_OR_EQUAL":
        return Op.gte;
      case "LOWER_THAN":
        return Op.lt;
      case "LOWER_THAN_OR_EQUAL":
        return Op.lte;
      case "EQUALS":
        return Op.eq;
      default:
        return Op.eq;
    }
  }

  private transformValue(operator: symbol, value: any): any {
    if (operator === Op.like || operator === Op.notLike) {
      return `%${value}%`;
    }
    return value;
  }
}
