package com.arms.egovframework.javaservice.esframework.factory;

import com.arms.egovframework.javaservice.esframework.esquery.EsQuery;
import com.arms.egovframework.javaservice.esframework.factory.builder.AbstractQueryBuilder;
import com.arms.egovframework.javaservice.esframework.factory.creator.AggregationQueryCountCreator;
import com.arms.egovframework.javaservice.esframework.factory.creator.AggregationQueryCreator;
import com.arms.egovframework.javaservice.esframework.factory.creator.AggregationQuerySumCreator;
import com.arms.egovframework.javaservice.esframework.factory.creator.DateAggregationQueryCreator;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.MainGroupDTO;
import com.arms.egovframework.javaservice.esframework.factory.builder.AggregationQuery;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SingleValueGroupDTO;
import com.arms.egovframework.javaservice.esframework.model.entity.BaseEntity;
import lombok.Setter;
import org.opensearch.data.client.orhlc.NativeSearchQuery;

@Setter
public class AggregationQueryFactory implements AbstractQueryBuilder<NativeSearchQuery> {

	private final AggregationQuery<NativeSearchQuery> aggregationQuery;

	private AggregationQueryFactory(AggregationQuery<NativeSearchQuery> aggregationQuery){
		this.aggregationQuery = aggregationQuery;
	}

	// ========================
	// nested (bucket 중첩 구조)
	// ========================

	public static AbstractQueryBuilder<NativeSearchQuery> nested(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			AggregationQueryCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nested(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			AggregationQueryCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithCount(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCountCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithCount(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCountCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithSum(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQuerySumCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithSum(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQuerySumCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithDay(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithDay(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithWeek(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithWeek(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithMonth(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithMonth(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyNestedAggregation();
		return subAggregationQueryCreator;
	}

	// ========================
	// flat (단일 레벨 bucket)
	// ========================

	public static AbstractQueryBuilder<NativeSearchQuery> flat(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			AggregationQueryCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flat(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			AggregationQueryCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithCount(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCountCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithCount(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCountCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithSum(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQuerySumCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithSum(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQuerySumCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithDay(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithDay(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithWeek(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithWeek(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithMonth(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> flatWithMonth(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyAggregation();
		return subAggregationQueryCreator;
	}

	// ========================
	// metric (leaf - 하위 집계 불가)
	// ========================

	public static AbstractQueryBuilder<NativeSearchQuery> metric(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCreator.of(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metric(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				AggregationQueryCreator.of(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithDay(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithDay(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.day(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithWeek(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithWeek(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.week(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithMonth(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, esQuery));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	public static AbstractQueryBuilder<NativeSearchQuery> metricWithMonth(MainGroupDTO mainGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
			DateAggregationQueryCreator.month(mainGroupDTO, mainGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyMetricAggregation();
		return subAggregationQueryCreator;
	}

	@Deprecated
	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithMetric(MainGroupDTO mainGroupDTO, EsQuery esQuery){
		return metric(mainGroupDTO, esQuery);
	}

	@Deprecated
	public static AbstractQueryBuilder<NativeSearchQuery> nestedWithMetric(MainGroupDTO mainGroupDTO){
		return metric(mainGroupDTO);
	}

	// ========================
	// cardinality (leaf - 하위 집계 불가)
	// ========================

	public static AbstractQueryBuilder<NativeSearchQuery> cardinalityWithDay(SingleValueGroupDTO singleValueGroupDTO){
		AggregationQueryFactory subAggregationQueryCreator = new AggregationQueryFactory(
				DateAggregationQueryCreator.day(singleValueGroupDTO, singleValueGroupDTO.getEsQuery()));
		subAggregationQueryCreator.applyCardinalityAggregation();
		return subAggregationQueryCreator;
	}

	// ========================
	// private apply methods
	// ========================

	private void applyNestedAggregation(){
		aggregationQuery.applyNestedAggregation();
	}

	private void applyCardinalityAggregation(){
		aggregationQuery.applyCardinalityAggregation();
	}

	private void applyMetricAggregation(){
		aggregationQuery.applyMetricAggregation();
	}

	private void applyAggregation(){
		aggregationQuery.applyAggregation();
	}

	@Override
	public NativeSearchQuery create() {
		return aggregationQuery.create();
	}

	@Override
	public NativeSearchQuery createForRecentTrue(Class<? extends BaseEntity> entityClass) {
		return aggregationQuery.createForRecentTrue(entityClass);
	}

}
