package com.arms.egovframework.javaservice.esframework.repository.common;

import com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery;
import com.arms.egovframework.javaservice.esframework.factory.AggregationQueryFactory;
import com.arms.egovframework.javaservice.esframework.factory.CompositeAggregationQueryFactory;
import com.arms.egovframework.javaservice.esframework.factory.SearchDocQueryFactory;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.CompositeGroupDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.MainGroupDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SearchDocDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SingleValueGroupDTO;
import com.arms.egovframework.javaservice.esframework.model.entity.BaseEntity;
import com.arms.egovframework.javaservice.esframework.model.vo.CatIndexVO;
import com.arms.egovframework.javaservice.esframework.model.vo.CompositeAggregationResultWrapper;
import com.arms.egovframework.javaservice.esframework.model.vo.DocumentAggregations;
import com.arms.egovframework.javaservice.esframework.model.vo.DocumentResultWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.elasticsearch.core.query.Query;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

@Slf4j
public class EsCommonRepositoryWrapper<E extends BaseEntity> {

    private final EsCommonRepository<E, ?> esCommonRepository;

    private final Class<E> entityClass;

    public EsCommonRepositoryWrapper(EsCommonRepository<E, ?> repository, Class<E> entityClass) {
        this.esCommonRepository = repository;
        this.entityClass = entityClass;
    }

    @SuppressWarnings("unchecked")
    private EsCommonRepository<E, Serializable> getEsCommonRepository() {
        return (EsCommonRepository<E, Serializable>) esCommonRepository;
    }

    // ====================================
    // normal aggregate
    // ====================================

    public DocumentAggregations aggregate(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nested(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecent(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nested(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateCount(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithCount(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentCount(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithCount(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateSum(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithSum(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentSum(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithSum(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateByDay(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithDay(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentByDay(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithDay(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateByWeek(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithWeek(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentByWeek(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithWeek(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateByMonth(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithMonth(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentByMonth(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.nestedWithMonth(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    // ====================================
    // metric aggregate
    // ====================================

    public DocumentAggregations aggregateMetric(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metric(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentMetric(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metric(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateMetricByDay(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithDay(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentMetricByDay(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithDay(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateMetricByWeek(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithWeek(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentMetricByWeek(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithWeek(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentAggregations aggregateMetricByMonth(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithMonth(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentMetricByMonth(SimpleQuery<MainGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.metricWithMonth(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    // ====================================
    // cardinality aggregate
    // ====================================

    public DocumentAggregations aggregateCardinalityByDay(SimpleQuery<SingleValueGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.cardinalityWithDay(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations aggregateRecentCardinalityByDay(SimpleQuery<SingleValueGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(AggregationQueryFactory.cardinalityWithDay(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    // ====================================
    // composite
    // ====================================

    public CompositeAggregationResultWrapper<E> compositeHits(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        return getEsCommonRepository().compositeHits(CompositeAggregationQueryFactory.composite(simpleQuery.toSearchDoc()).create());
    }

    public CompositeAggregationResultWrapper<E> compositeRecentHits(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        return getEsCommonRepository().compositeHits(CompositeAggregationQueryFactory.composite(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public CompositeAggregationResultWrapper<E> compositeAllHits(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        Function<CompositeGroupDTO, Query> function = (searchDoc)->CompositeAggregationQueryFactory.composite(searchDoc).create();
        return getEsCommonRepository().compositeAllHits(simpleQuery.toSearchDoc(),function);
    }

    public CompositeAggregationResultWrapper<E> compositeRecentAllHits(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        Function<CompositeGroupDTO, Query> function = (searchDoc)->CompositeAggregationQueryFactory.composite(searchDoc).createForRecentTrue(entityClass);
        return getEsCommonRepository().compositeAllHits(simpleQuery.toSearchDoc(),function);
    }

    public DocumentAggregations compositeAggregation(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(CompositeAggregationQueryFactory.composite(simpleQuery.toSearchDoc()).create());
    }

    public DocumentAggregations compositeAggregationRecent(SimpleQuery<CompositeGroupDTO> simpleQuery) {
        return getEsCommonRepository().aggregateDocs(CompositeAggregationQueryFactory.composite(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    // ====================================
    // find (검색)
    // ====================================

    public DocumentResultWrapper<E> findRecentHits(SimpleQuery<SearchDocDTO> simpleQuery) {
        return getEsCommonRepository().findRecentHits(SearchDocQueryFactory.searchDoc(simpleQuery.toSearchDoc()).createForRecentTrue(entityClass));
    }

    public DocumentResultWrapper<E> findHits(SimpleQuery<SearchDocDTO> simpleQuery){
        return getEsCommonRepository().findHits(SearchDocQueryFactory.searchDoc(simpleQuery.toSearchDoc()).create());
    }

    public E findRecentDocByRecentId(Serializable serializable) {
        return getEsCommonRepository().findRecentDocByRecentId(serializable);
    }

    public <U extends Serializable> E findDocById(U id) {
        return getEsCommonRepository().findDocById(id);
    }

    public <U extends Serializable> DocumentResultWrapper<E> findRecentDocById(U id) {
        return getEsCommonRepository().findRecentDocById(id);
    }

    public List<E> findAllDocsBySearchAfter(SimpleQuery<SearchDocDTO> simpleQuery) {
        Function<SearchDocDTO, Query> function = (searchDoc)->SearchDocQueryFactory.searchDoc(searchDoc).create();
        return getEsCommonRepository().findAllDocsBySearchAfter(simpleQuery.toSearchDoc(), function);
    }

    public List<E> findAllRecentDocsBySearchAfter(SimpleQuery<SearchDocDTO> simpleQuery) {
        Function<SearchDocDTO, Query> function = (searchDoc)->SearchDocQueryFactory.searchDoc(searchDoc).createForRecentTrue(entityClass);
        return getEsCommonRepository().findAllRecentDocsBySearchAfter(simpleQuery.toSearchDoc(), function);
    }

    public DocumentResultWrapper<E> findAllHits() {
        return getEsCommonRepository().findAllHits();
    }

    public DocumentResultWrapper<E> findRecentAllHits() {
        return getEsCommonRepository().findRecentAllHits();
    }

    // ====================================
    // index 관리
    // ====================================

    public String indexAliasName(){
        return getEsCommonRepository().indexAliasName();
    }

    public void deleteIndex(int days){
        getEsCommonRepository().deleteIndex(days);
    }

    public void deleteIndex(Map<String,List<String>> indexNameGroupByMonth){
        getEsCommonRepository().deleteIndex(indexNameGroupByMonth);
    }

    public void merge(int day){
        getEsCommonRepository().merge(day);
    }

    public void reindex(Map<String,List<String>> indexNameGroupByMonth){
        getEsCommonRepository().reindex(indexNameGroupByMonth);
    }

    // ====================================
    // CRUD
    // ====================================

    public E save(E entity){
        return getEsCommonRepository().save(entity);
    }

    public Iterable<E> saveAll(Iterable<E> entities){
        return getEsCommonRepository().saveAll(entities);
    }

    public E modifyWithIndexName(E entity,String indexName){
        return getEsCommonRepository().modifyWithIndexName(entity,indexName);
    }

    public E saveEmpty(E entity){
        return getEsCommonRepository().saveEmpty(entity);
    }

    public <U extends Serializable> String deleteById(U u) {
        E docById = this.findDocById(u);
        return getEsCommonRepository().deleteDocByEntity(docById);
    }

    public <U extends Serializable> Serializable deleteRecentDocById(U u) {
        return getEsCommonRepository().deleteRecentDocById(u);
    }

    public void deleteAll(){
        getEsCommonRepository().deleteAll();
    }

    public void deleteAllById(Iterable<String> ids){
        getEsCommonRepository().deleteAllById(ids);
    }

    public List<CatIndexVO> catIndexVOList() {
         return getEsCommonRepository().catIndexVOList();
    }

    public Long indexAllCount() {
        return getEsCommonRepository().indexCount();
    }

}
