package com.oguerreiro.resilient.service.query;

import java.util.List;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.oguerreiro.resilient.domain.Variable;
import com.oguerreiro.resilient.domain.Variable_;
import com.oguerreiro.resilient.repository.VariableRepository;
import com.oguerreiro.resilient.service.criteria.VariableCriteria;
import com.oguerreiro.resilient.service.dto.VariableDTO;
import com.oguerreiro.resilient.service.mapper.VariableMapper;

import jakarta.persistence.criteria.JoinType;

/**
 * Service for executing complex queries for {@link Variable} entities in the database.
 * The main input is a {@link VariableCriteria} which gets converted to {@link Specification},
 * in a way that all the filters must apply.
 * It returns a {@link List} of {@link VariableDTO} which fulfills the criteria.
 */
@Service
@Transactional(readOnly = true)
public class VariableQueryService extends AbstractQueryService<Variable, VariableDTO, Long, VariableCriteria> {

  public VariableQueryService(VariableRepository variableRepository, VariableMapper variableMapper) {
    super(variableRepository, variableMapper);
  }

  /**
   * Function to convert {@link VariableCriteria} to a {@link Specification}
   * 
   * @param  criteria The object which holds all the filters, which the entities should match.
   * @return          the matching {@link Specification} of the entity.
   */
  protected Specification<Variable> createSpecification(VariableCriteria criteria) {
    Specification<Variable> specification = Specification.where(null);
    if (criteria != null) {
      // This has to be called first, because the distinct method returns null
      if (criteria.getDistinct() != null) {
        specification = specification.and(distinct(criteria.getDistinct()));
      }
      if (criteria.getId() != null) {
        specification = specification.and(buildRangeSpecification(criteria.getId(), Variable_.id));
      }
      if (criteria.getCode() != null) {
        specification = specification.and(buildStringSpecification(criteria.getCode(), Variable_.code));
      }
      if (criteria.getVariableIndex() != null) {
        specification = specification.and(
            buildRangeSpecification(criteria.getVariableIndex(), Variable_.variableIndex));
      }
      if (criteria.getName() != null) {
        specification = specification.and(buildStringSpecification(criteria.getName(), Variable_.name));
      }
      if (criteria.getDescription() != null) {
        specification = specification.and(buildStringSpecification(criteria.getDescription(), Variable_.description));
      }
      if (criteria.getInput() != null) {
        specification = specification.and(buildSpecification(criteria.getInput(), Variable_.input));
      }
      if (criteria.getInputMode() != null) {
        specification = specification.and(buildSpecification(criteria.getInputMode(), Variable_.inputMode));
      }
      if (criteria.getOutput() != null) {
        specification = specification.and(buildSpecification(criteria.getOutput(), Variable_.output));
      }
      if (criteria.getOutputFormula() != null) {
        specification = specification.and(
            buildStringSpecification(criteria.getOutputFormula(), Variable_.outputFormula));
      }
      if (criteria.getVersion() != null) {
        specification = specification.and(buildRangeSpecification(criteria.getVersion(), Variable_.version));
      }
      if (criteria.getVariableUnitsId() != null) {
        specification = specification.and(buildSpecification(criteria.getVariableUnitsId(),
            root -> root.join(Variable_.variableUnits, JoinType.LEFT).get(ID_ATTRIBUTE_NAME)));
      }
      if (criteria.getBaseUnitId() != null) {
        specification = specification.and(buildSpecification(criteria.getBaseUnitId(),
            root -> root.join(Variable_.baseUnit, JoinType.LEFT).get(ID_ATTRIBUTE_NAME)));
      }
      if (criteria.getVariableScopeId() != null) {
        specification = specification.and(buildSpecification(criteria.getVariableScopeId(),
            root -> root.join(Variable_.variableScope, JoinType.LEFT).get(ID_ATTRIBUTE_NAME)));
      }
      if (criteria.getVariableCategoryId() != null) {
        specification = specification.and(buildSpecification(criteria.getVariableCategoryId(),
            root -> root.join(Variable_.variableCategory, JoinType.LEFT).get(ID_ATTRIBUTE_NAME)));
      }
    }
    return specification;
  }
}
