package com.ecep.contract;

import java.util.List;

import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.MyRepository;
import com.ecep.contract.model.Voable;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * 实体服务基类
 * 
 * @param <T>  实体类型
 * @param <VO> VO类型
 * @param <ID> 主键类型
 */
public abstract class EntityService<T extends Voable<VO>, VO, ID> {

    protected abstract MyRepository<T, ID> getRepository();

    public T getById(ID id) {
        return getRepository().findById(id).orElse(null);
    }

    public abstract T createNewEntity();

    public long count() {
        return getRepository().count();
    }

    public long count(Specification<T> spec) {
        return getRepository().count(spec);
    }

    public long count(JsonNode paramsNode) {
        return getRepository().count(buildParameterSpecification(paramsNode));
    }

    protected abstract Specification<T> buildParameterSpecification(JsonNode paramsNode);

    public Page<VO> findAll(JsonNode paramsNode, Pageable pageable) {
        Specification<T> spec = null;
        if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
            spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
        }
        spec = SpecificationUtils.and(spec, buildParameterSpecification(paramsNode));
        return findAll(spec, pageable).map(T::toVo);
    }

    public Page<T> findAll(Specification<T> spec, Pageable pageable) {
        return getRepository().findAll(spec, pageable);
    }

    public List<T> findAll(Specification<T> spec, Sort sort) {
        return getRepository().findAll(spec, sort);
    }

    protected abstract Specification<T> buildSearchSpecification(String searchText);

    public Specification<T> getSpecification(String searchText) {
        if (!StringUtils.hasText(searchText)) {
            return null;
        }
        return SpecificationUtils.andWith(searchText, this::buildSearchSpecification);
    }

    public List<T> search(String searchText) {
        Specification<T> spec = getSpecification(searchText);
        return getRepository().findAll(spec, Pageable.ofSize(10)).getContent();
    }
}
