
package com.tmerclub.cloud.search.builder;

import com.tmerclub.cloud.api.dto.EsPageDTO;
import com.tmerclub.cloud.api.vo.EsPageVO;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.i18n.LanguageEnum;
import com.tmerclub.cloud.common.product.vo.search.*;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.search.constant.EsConstant;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedTopHits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author YXF
 * @date 2021/07/29
 */
@Component
public class ProductSearchResponseBuilder {

    private static final Logger log = LoggerFactory.getLogger(ProductSearchResponseBuilder.class);

    /**
     *
     */
    public List<ProductSearchVO> getProductSearchList(SearchResponse response) {

        // 1、返回的所有查询到的商品
        ProductSearchVO productSearchVO = new ProductSearchVO();
        //===============spu列表信息====================//
        productSearchVO.setSpus(buildSpuSearchList(response.getHits()));

        //===============聚合信息====================//
        Aggregations aggregations = response.getAggregations();
        if (Objects.nonNull(aggregations)) {
            processingAggregationsData(productSearchVO, aggregations);
        }

        List<ProductSearchVO> productSearches = new ArrayList<>(Constant.INITIAL_CAPACITY);
        productSearches.add(productSearchVO);
        return productSearches;
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    public List<SpuSearchVO> buildSpuSearchList(SearchHits hits) {
        String spuName;
        String sellingPoin;
        if (Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
            spuName = EsConstant.SPU_NAME_ZH;
            sellingPoin = EsConstant.SELLING_POINT_ZH;
        } else {
            spuName = EsConstant.SPU_NAME_EN;
            sellingPoin = EsConstant.SELLING_POINT_EN;
        }
        List<SpuSearchVO> spus = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (SearchHit hit : hits.getHits()) {
            String json = hit.getSourceAsString();
            SpuSearchVO spuSearchVO = Json.parseObject(json, SpuSearchVO.class);
            spuSearchVO.setSpuName(handleAggregationsLang(json, spuName, EsConstant.SPU_NAME_ZH));
            spuSearchVO.setSellingPoint(handleAggregationsLang(json, sellingPoin, EsConstant.SELLING_POINT_ZH));
            spus.add(spuSearchVO);
        }
        return spus;
    }

    /**
     * 处理聚合数据
     *
     * @param productSearchVO
     * @param aggregations
     */
    private void processingAggregationsData(ProductSearchVO productSearchVO, Aggregations aggregations) {
        String brandName;
        String categoryName;
        String attrName;
        String attrValueName;
        if (Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
            brandName = EsConstant.BRAND_NAME_ZH;
            categoryName = EsConstant.CATEGORY_NAME_ZH;
            attrName = EsConstant.ATTR_NAME_ZH;
            attrValueName = EsConstant.ATTR_VALUE_NAME_ZH;
        } else {
            brandName = EsConstant.BRAND_NAME_EN;
            categoryName = EsConstant.CATEGORY_NAME_EN;
            attrName = EsConstant.ATTR_NAME_EN;
            attrValueName = EsConstant.ATTR_VALUE_NAME_EN;
        }
        //===============品牌信息====================//
        ParsedNested brandNested = aggregations.get(EsConstant.BRAND);
        if (Objects.nonNull(brandNested)) {
            productSearchVO.setBrands(new ArrayList<>(Constant.INITIAL_CAPACITY));
            List<String> brandJson = handleAggregations(brandNested, EsConstant.BRAND_ID);
            for (String json : brandJson) {
                BrandSearchVO brandSearchVO = Json.parseObject(json, BrandSearchVO.class);
                brandSearchVO.setBrandName(handleAggregationsLang(json, brandName, EsConstant.BRAND_NAME_ZH));
                productSearchVO.getBrands().add(brandSearchVO);
            }
        }
        //===============分类信息====================//
        loadCategoryAggregationsData(productSearchVO, aggregations, categoryName);

        //===============店铺信息====================//
        ParsedLongTerms shopTerms = aggregations.get(EsConstant.SHOP);
        if (Objects.nonNull(shopTerms)) {
            List<? extends Terms.Bucket> shopBuckets = shopTerms.getBuckets();
            for (Terms.Bucket bucket : shopBuckets) {
                productSearchVO.setShopInfo(new ShopInfoSearchVO());
                productSearchVO.getShopInfo().setShopId(Long.valueOf(bucket.getKey().toString()));
            }
        }
        //===============属性信息====================//
        loadAttrAggregationsData(productSearchVO, aggregations, attrName, attrValueName);
    }

    private void loadCategoryAggregationsData(ProductSearchVO productSearchVO, Aggregations aggregations, String categoryName) {
        productSearchVO.setCategorys(new ArrayList<>(Constant.INITIAL_CAPACITY));
        String categoryId;
        ParsedNested categoriesNested;
        // 平台分类
        if (Objects.nonNull(aggregations.get(EsConstant.CATEGORY))) {
            categoryId = EsConstant.CATEGORY_ID;
            categoriesNested = aggregations.get(EsConstant.CATEGORY);
        }
        // 店铺分类
        else {
            categoryId = EsConstant.SHOP_CATEGORY_ID;
            categoriesNested = aggregations.get(EsConstant.SHOP_CATEGORY);
        }
        if (Objects.nonNull(categoriesNested)) {
            productSearchVO.setCategorys(new ArrayList<>(Constant.INITIAL_CAPACITY));
            List<String> categoryJson = handleAggregations(categoriesNested, categoryId);
            for (String json : categoryJson) {
                CategorySearchVO categorySearchVO = Json.parseObject(json, CategorySearchVO.class);
                categorySearchVO.setName(handleAggregationsLang(json, categoryName, EsConstant.CATEGORY_NAME_ZH));
                productSearchVO.getCategorys().add(categorySearchVO);
            }
        }
    }

    private void loadAttrAggregationsData(ProductSearchVO productSearchVO, Aggregations aggregations, String attrName, String attrValueName) {
        productSearchVO.setAttrs(new ArrayList<>(Constant.INITIAL_CAPACITY));
        ParsedNested attrsNested = aggregations.get(EsConstant.ATTRS);
        if (Objects.nonNull(attrsNested)) {
            Aggregations attrIdAggregations = attrsNested.getAggregations();
            ParsedLongTerms attrIdsTrems = attrIdAggregations.get(EsConstant.ATTR_IDS);
            List<? extends Terms.Bucket> attrsBuckets = attrIdsTrems.getBuckets();
            for (Terms.Bucket bucket : attrsBuckets) {
                ParsedLongTerms attrLongTerms = bucket.getAggregations().get(EsConstant.ATTR_VALUE_IDS);
                AttrSearchVO attrSearchVO = null;
                for (Terms.Bucket attrValueBucket : attrLongTerms.getBuckets()) {
                    ParsedTopHits parsedTopHits = attrValueBucket.getAggregations().get(EsConstant.TOP_HITS_DATA);
                    for (SearchHit hit : parsedTopHits.getHits().getHits()) {
                        String json = hit.getSourceAsString();
                        if (Objects.isNull(attrSearchVO)) {
                            attrSearchVO = Json.parseObject(json, AttrSearchVO.class);
                            attrSearchVO.setAttrId(Long.valueOf(bucket.getKey().toString()));
                            attrSearchVO.setAttrName(handleAggregationsLang(json, attrName, EsConstant.ATTR_NAME_ZH));
                            attrSearchVO.setAttrValues(new ArrayList<>(0));
                        }
                        AttrValueSearchVO attrValueSearchVO = Json.parseObject(hit.getSourceAsString(), AttrValueSearchVO.class);
                        attrValueSearchVO.setAttrValueName(handleAggregationsLang(json, attrValueName, EsConstant.ATTR_VALUE_NAME_ZH));
                        attrSearchVO.getAttrValues().add(attrValueSearchVO);
                    }
                }
                productSearchVO.getAttrs().add(attrSearchVO);
            }
        }
    }

    /**
     * 处理聚合数据
     *
     * @param brandNested
     * @return
     */
    public List<String> handleAggregations(ParsedNested brandNested, String id) {
        List<String> dataList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        ParsedLongTerms brandLongTerms = brandNested.getAggregations().get(id);
        List<? extends Terms.Bucket> buckets = brandLongTerms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            ParsedTopHits parsedTopHits = bucket.getAggregations().get(EsConstant.TOP_HITS_DATA);
            for (SearchHit hit : parsedTopHits.getHits().getHits()) {
                String sourceAsString = hit.getSourceAsString();
                dataList.add(sourceAsString);
            }
        }
        return dataList;
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param response es返回的数据
     * @return
     */
    public List<SpuAdminVO> buildSpuAdminList(SearchResponse response) {
        List<SpuAdminVO> spus = new ArrayList<>(Constant.INITIAL_CAPACITY);
        String spuName;
        if (Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
            spuName = EsConstant.SPU_NAME_ZH;
        } else {
            spuName = EsConstant.SPU_NAME_EN;
        }
        for (SearchHit hit : response.getHits()) {
            String json = hit.getSourceAsString();
            SpuAdminVO spuSearchVO = Json.parseObject(json, SpuAdminVO.class);
            spuSearchVO.setSpuName(handleAggregationsLang(json, spuName, EsConstant.SPU_NAME_ZH));
            spus.add(spuSearchVO);
        }
        return spus;
    }

    /**
     * 从聚合数据中获取商品列表
     *
     * @param response
     * @return
     */
    public Map<Long, List<SpuSearchVO>> loadSpuMapByAggregations(SearchResponse response) {
        Aggregations aggregations = response.getAggregations();
        if (Objects.isNull(aggregations.getAsMap())) {
            return new HashMap<>(0);
        }
        ParsedLongTerms shopTerm = aggregations.get(EsConstant.SPU_LIST);
        Map<Long, List<SpuSearchVO>> spuMap = new HashMap<>(10);
        if (Objects.nonNull(shopTerm)) {
            List<? extends Terms.Bucket> buckets = shopTerm.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                Aggregations shopAgg = bucket.getAggregations();
                ParsedTopHits shopHits = shopAgg.get(EsConstant.TOP_HITS_DATA);
                spuMap.put(Long.valueOf(bucket.getKey().toString()), buildSpuSearchList(shopHits.getHits()));
            }
        }
        return spuMap;
    }

    /**
     * 构建分页数据
     *
     * @param pageDTO
     * @param esPageVO
     * @param response
     */
    public void buildSearchPage(EsPageDTO pageDTO, EsPageVO<?> esPageVO, SearchResponse response) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int) total % pageDTO.getPageSize() == 0 ?
                (int) total / pageDTO.getPageSize() : ((int) total / pageDTO.getPageSize() + 1);
        esPageVO.setPages(totalPages);
    }

    /**
     * 处理聚合国际化信息
     *
     * @param json  数据
     * @param field 字段
     * @return 对应语言的字段
     */
    @SuppressWarnings("unchecked")
    private String handleAggregationsLang(String json, String field, String defaultField) {
        Map<String, Object> map = Json.parseObject(json, Map.class);
        Object object;
        // 找不到指定语言的数据，就查默认语言
        if (Objects.isNull(map.get(field))) {
            object = map.get(defaultField);
        }
        // 获取指定语言的数据
        else {
            object = map.get(field);
        }
        // 没有查到数据
        if (Objects.isNull(object)) {
            return null;
        }
        return object.toString();
    }

}
