package io.delphiplatform.api.v3.rdb.service.specification;

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

import java.util.List;

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

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.util.JpaUtils;
import io.delphiplatform.api.v3.rdb.service.dto.JpaPropertyPath;

public abstract class OperationOnPropertySpecification<T> implements Specification<T> {

    private final List<String> joins;
    private final boolean doDistinct;

    public OperationOnPropertySpecification(List<String> joins) {
        this(joins, true);
    }

    public OperationOnPropertySpecification(List<String> joins, boolean doDistinct) {
        this.joins = joins;
        this.doDistinct = doDistinct;
    }

    @Override
    public Predicate toPredicate(Root<T> r, CriteriaQuery<?> cq, CriteriaBuilder cb) {
        if (canProducePredicate()) {
            if (CollectionUtils.isEmpty(joins)) {
                return doOperation(cb, r);
            } else {
                Path<T> path = JpaUtils.getPathWithJoins(r, JpaPropertyPath.ofJoins(joins));
                if (doDistinct) {
                    cq.distinct(true);
                }
                return doOperation(cb, path);
            }
        }
        return null;
    }

    protected abstract boolean canProducePredicate();

    protected abstract Predicate doOperation(CriteriaBuilder cb, Path<T> path);

}
