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

import com.arms.egovframework.javaservice.esframework.annotation.Recent;
import com.arms.egovframework.javaservice.esframework.esquery.EsQuery;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.SearchDocDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.MainGroupDTO;

import com.arms.egovframework.javaservice.esframework.model.entity.BaseEntity;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.opensearch.search.sort.FieldSortBuilder;
import org.springframework.data.domain.PageRequest;
import org.opensearch.data.client.orhlc.NativeSearchQuery;
import org.opensearch.data.client.orhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.DocValueField;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.ArrayList;

import static com.arms.egovframework.javaservice.esframework.util.ReflectionUtil.fieldInfo;


public class SearchQueryBuilder implements AbstractQueryBuilder<NativeSearchQuery> {

	private final NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
	private final BoolQueryBuilder boolQuery;

	private SearchQueryBuilder(SearchDocDTO searchDocDTO, EsQuery esQuery){

		String[] fieldIncludes = searchDocDTO.getFieldIncludes();
		int size = searchDocDTO.getSize();
		int page = searchDocDTO.getPage();
		boolean isPaging = searchDocDTO.isPaging();
		List<Object> searchAfter = Optional.ofNullable(searchDocDTO.getSearchAfter()).orElse(new ArrayList<>());
		boolean fetchSource = (fieldIncludes != null && fieldIncludes.length > 0);
		List<FieldSortBuilder> fieldSortBuilders = esQuery.getSortBuilders();
		HighlightBuilder highlightBuilder = esQuery.getHighlightBuilder();

		this.boolQuery = esQuery.getBoolQueryBuilder();
		this.nativeSearchQueryBuilder.withMaxResults(size);

		if(isPaging){
			nativeSearchQueryBuilder.withPageable(PageRequest.of(page, size));
		}

		if(fetchSource){
			nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(false, null, null));
			nativeSearchQueryBuilder.withDocValueFields(Arrays.stream(fieldIncludes).map(DocValueField::new).toList());
		}

		Optional.ofNullable(boolQuery)
			.ifPresent(nativeSearchQueryBuilder::withQuery);

		Optional.ofNullable(highlightBuilder)
			.ifPresent(nativeSearchQueryBuilder::withHighlightBuilder);

		Optional.ofNullable(fieldSortBuilders)
			.ifPresent(sorts -> sorts.forEach(nativeSearchQueryBuilder::withSorts));

		if(!searchAfter.isEmpty()){
			nativeSearchQueryBuilder.withSearchAfter(searchAfter);
		}
	}

	public static SearchQueryBuilder of(SearchDocDTO searchDocDTO, EsQuery esQuery){
		return new SearchQueryBuilder(searchDocDTO, esQuery);
	}

	public static SearchQueryBuilder of(EsQuery esQuery){
		return new SearchQueryBuilder(new MainGroupDTO() {},esQuery);
	}

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

	@Override
	public NativeSearchQuery createForRecentTrue(Class<? extends BaseEntity> entityClass) {
		String recentFieldName = fieldInfo(entityClass, Recent.class).getName();
		boolQuery.filter(QueryBuilders.termQuery(recentFieldName,true));
		return nativeSearchQueryBuilder.build();
	}


}
