package com.fira.one.universel.criteria;

import java.util.Arrays;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.data.jpa.domain.Specification;

public class GenericSpecification<T> implements Specification<T> {

    private transient SearchCriteria criteria;

    public GenericSpecification() {
    }

    public GenericSpecification(SearchCriteria criteria) {
        this.criteria = criteria;
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        Expression<String> propertyPath;

        if (criteria.isSubReference()) {
            List<String> subKeys = Arrays.asList(criteria.getKey().split(SearchCriteria.SUB_REFERENCE_SEP_SPLIT));
            Path<String> path = root.get(subKeys.get(0));
            for (var i = 1; i < subKeys.size(); i++) {
                path = path.get(subKeys.get(i));
            }
            propertyPath = path;
        } else {
            propertyPath = root.get(criteria.getKey());
        }

        switch (criteria.getOperation()) {

            case SearchOperation.GREATER_THAN:
                return cb.greaterThanOrEqualTo(propertyPath, criteria.getValue().toString());

            case SearchOperation.LESS_THAN:
                return cb.lessThanOrEqualTo(propertyPath, criteria.getValue().toString());

            case SearchOperation.CASE_INSENSITIVE_LIKE:
                return cb.like(cb.lower(propertyPath), criteria.getValue().toString().toLowerCase());

            case SearchOperation.LIKE:
                return cb.like(propertyPath, criteria.getValue().toString());

            case SearchOperation.EQUAL:
                return cb.equal(propertyPath, criteria.getValue());

            case SearchOperation.IN:
                return propertyPath.in(criteria.getValue());

            default:
                return null;

        }
    }

}