package com.arms.egovframework.javaservice.esframework.model.vo;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;
import org.opensearch.search.SearchHit;
import org.opensearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
import org.opensearch.search.aggregations.metrics.NumericMetricsAggregation;
import org.opensearch.search.aggregations.metrics.TopHits;


@Slf4j
public class DocumentBucket {

    private String groupKey;

    private String docFieldName;

    private Object docFieldObject;

    private String docCount;

    private List<DocumentBucket> subDocuments;

    private DocumentBucket parentDocumentBucket;

    private List<SearchHit> searchHits;

    private DocumentBucket() {
    }

    void setParentDocumentBucket(DocumentBucket parentDocumentBucket) {
        this.parentDocumentBucket = parentDocumentBucket;
    }

    private <T extends Number> T getDocCount(Class<T> t){
        if(t.equals(Float.class)) {
            return t.cast(Float.valueOf(docCount));
        }
        if(t.equals(Long.class)) {
            return t.cast(getDocCount());
        }
        throw new IllegalArgumentException("지원하지 않는 타입 입니다.");
    }

    Long getDocCount() {
        return Float.valueOf(docCount).longValue();
    }

    List<DocumentBucket> getDeepestDocument() {

        if (subDocuments == null || subDocuments.isEmpty()) {
            return List.of(this);
        }

        subDocuments
                .forEach(bucket -> bucket.setParentDocumentBucket(this));

        return subDocuments.stream()
                .flatMap(bucket -> {
                    List<DocumentBucket> deepestList = bucket.getDeepestDocument();
                    return deepestList.stream();
                })
                .collect(Collectors.toList());
    }

    public Object objectByName(String docAliasName) {

        if (parentDocumentBucket == null&&docAliasName.equals(groupKey)) {
            return docFieldObject;
        }

        if (parentDocumentBucket == null) {
            return "";
        }

        if(docAliasName.equals(groupKey)) {
            return docFieldObject;
        }

        return parentDocumentBucket.valueByName(docAliasName);
    }

    public String valueByName(String docAliasName) {

        if (parentDocumentBucket == null&&docAliasName.equals(groupKey)) {
            return docFieldName;
        }

        if (parentDocumentBucket == null) {
            return "";
        }

        if(docAliasName.equals(groupKey)) {
            return docFieldName;
        }

        return parentDocumentBucket.valueByName(docAliasName);
    }

    private  <T extends Number> T countByName(String docAliasName, Class<T> t) {

        if (parentDocumentBucket == null&&docAliasName.equals(groupKey)) {
            return getDocCount(t);
        }

        if (parentDocumentBucket == null) {
            return t.cast(0);
        }

        if(docAliasName.equals(groupKey)) {
            return getDocCount(t);
        }

        return parentDocumentBucket.countByName(docAliasName,t);
    }

    public Float floatCountByName(String docAliasName) {
        return countByName(docAliasName,Float.class);
    }

    public Long countByName(String docAliasName) {

        if (parentDocumentBucket == null&&docAliasName.equals(groupKey)) {
            return getDocCount();
        }

        if (parentDocumentBucket == null) {
            return 0L;
        }

        if(docAliasName.equals(groupKey)) {
            return getDocCount();
        }

        return parentDocumentBucket.countByName(docAliasName);
    }

    DocumentBucket(String groupKey,Bucket bucket) {

        this.groupKey = groupKey;

        this.docFieldName = bucket.getKeyAsString();

        this.docFieldObject = bucket.getKey();

        this.docCount = String.valueOf(bucket.getDocCount());

        this.subDocuments = new DocumentAggregations(bucket.getAggregations().asMap()).docBuckets();

    }

    DocumentBucket(String groupKey, NumericMetricsAggregation.SingleValue bucketValue) {

        this.groupKey = groupKey;

        this.docFieldName = groupKey;

        this.docCount = bucketValue.getValueAsString();
    }

    DocumentBucket(String groupKey, TopHits topHits) {

        this.groupKey = groupKey;

        SearchHit[] hits = topHits.getHits().getHits();
        if (hits.length > 0) {
            this.docFieldName = hits[0].getId();
            this.docCount = String.valueOf(hits.length);
            this.searchHits = Arrays.asList(hits);
        } else {
            this.docFieldName = "";
            this.docCount = "0";
            this.searchHits = List.of();
        }
    }

    public List<SearchHit> getSearchHits() {
        return searchHits != null ? searchHits : List.of();
    }

}