package ru.micord.ervu_dashboard.dao;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.stream.Collectors;

import database.dao.DefaultLoadDao;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.springframework.beans.factory.annotation.Autowired;

import ru.cg.webbpm.modules.database.api.utils.QueryUtils;
import ru.cg.webbpm.modules.database.bean.entity_graph.condition.Operator;
import ru.cg.webbpm.modules.database.bean.filter.EntityFilter;
import ru.cg.webbpm.modules.database.bean.filter.FilterOperation;

/**
 * @author r.latypov
 */
public class CustomCoalesceDao extends DefaultLoadDao {
  @Autowired
  private DSLContext dsl;

  @Override
  public Condition getFilterCondition(EntityFilter entityFilter) {
    Field<Object> field = QueryUtils.toJooqField(
        entityFilter.getEntityColumn(), dsl.dialect(), false
    );
    return condition(entityFilter, field);
  }

  @Override
  public Condition getFilterCondition(EntityFilter entityFilter, String tableName) {
    Field<Object> field = toJooqField(entityFilter.getEntityColumn(), tableName);
    return condition(entityFilter, field);
  }

  private Condition condition(EntityFilter entityFilter, Field<Object> field) {
    FilterOperation operation = entityFilter.getFilterOperation();
    Operator multiValueOperator = entityFilter.getMultiValueOperator();
    Object value = entityFilter.getValue();
    // todo this is KOSTIL
    if (value instanceof String && field.getDataType().isDateTime()) {
      if (java.sql.Date.class.isAssignableFrom(field.getType())) {
        value = java.sql.Date.valueOf(
            LocalDate.parse(String.valueOf(value), DateTimeFormatter.ISO_DATE_TIME));
      }
      else if (Timestamp.class.isAssignableFrom(field.getType())) {
        value = Timestamp.valueOf(
            LocalDateTime.parse(String.valueOf(value), DateTimeFormatter.ISO_DATE_TIME));
      }
    }

    return switch (operation) {
      case EQUAL -> getEqualCondition(field, value, multiValueOperator);
      case NOT_EQUAL -> getNotEqualCondition(field, value, multiValueOperator);
      case EQUAL_IGNORE_CASE -> getEqualIgnoreCaseCondition(field, value, multiValueOperator);
      case GREATER_THAN -> getGreaterThanCondition(field, value, multiValueOperator);
      case LESS_THAN -> getLessThanCondition(field, value, multiValueOperator);
      case GREATER_OR_EQUAL -> getGreaterOrEqualCondition(field, value, multiValueOperator);
      case LESS_OR_EQUAL -> getLessOrEqualCondition(field, value, multiValueOperator);
      case CONTAINS -> getContainsCondition(field, value, multiValueOperator);
      case NOT_CONTAINS -> getNotContainsCondition(field, value, multiValueOperator);
      case START_WITH -> getStartsWithCondition(field, value, multiValueOperator);
      case ENDS_WITH -> getEndsWithCondition(field, value, multiValueOperator);
      case IS_NULL -> field.isNull();
      case IS_NOT_NULL -> field.isNotNull();
      case IN -> field.in(value instanceof String
                          ? Arrays.stream(((String) value).split(","))
                              .collect(Collectors.toUnmodifiableSet())
                          : value)
          .or(field.isNull());
      case NOT_IN -> field.notIn(value);
    };
  }
}

