
package com.tmerclub.cloud.search.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.product.dto.WarehouseSearchDTO;
import com.tmerclub.cloud.api.product.feign.WarehouseFeignClient;
import com.tmerclub.cloud.common.order.vo.WarehouseVO;
import com.tmerclub.cloud.api.dto.EsPageDTO;
import com.tmerclub.cloud.api.order.bo.EsPurchaseOrderBO;
import com.tmerclub.cloud.api.order.bo.EsPurchaseOrderItemBO;
import com.tmerclub.cloud.api.supplier.feign.SupplierDetailFeignClient;
import com.tmerclub.cloud.api.supplier.vo.SupplierApiDetailVO;
import com.tmerclub.cloud.api.vo.EsPageVO;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.i18n.LanguageEnum;
import com.tmerclub.cloud.common.order.dto.PurchaseOrderSearchDTO;
import com.tmerclub.cloud.common.order.vo.EsPurchaseOrderVO;
import com.tmerclub.cloud.common.product.constant.StockPointType;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.search.constant.EsIndexEnum;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tmerclub
 * @date 2020/11/16
 */
@Component
public class PurchaseOrderSearchManager {

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


    @Autowired
    private RestHighLevelClient restHighLevelClient;


    @DubboReference
    private SupplierDetailFeignClient supplierDetailFeignClient;
    @DubboReference
    private WarehouseFeignClient warehouseFeignClient;

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param param 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsPurchaseOrderVO> page(EsPageDTO pageDTO, PurchaseOrderSearchDTO param) {
        param.setPageSize(pageDTO.getPageSize());
        param.setPageNum(pageDTO.getPageNum());
        EsPageVO<EsPurchaseOrderVO> purchaseOrderPage = pageSearchResult(param);
        this.loadInfo(purchaseOrderPage.getList());
        return purchaseOrderPage;
    }

    /**
     * 补充信息
     * @param list 列表数据
     */
    private void loadInfo(List<EsPurchaseOrderVO> list) {
        if (CollUtil.isEmpty(list)) {
            return;
        }
        Set<Long> supplierIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> warehouseIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (EsPurchaseOrderVO purchaseOrderVO : list) {
            supplierIds.add(purchaseOrderVO.getSupplierId());
            warehouseIds.add(purchaseOrderVO.getWarehouseId());
        }
        // 查询供应商信息
        ServerResponseEntity<List<SupplierApiDetailVO>> supplierResp = supplierDetailFeignClient.listSupplierDetailBySupplierIds(new ArrayList<>(supplierIds));
        if (!supplierResp.isSuccess()) {
            throw new LuckException(supplierResp.getMsg());
        }
        Map<Long, SupplierApiDetailVO> supplierMap = supplierResp.getData().stream().collect(Collectors.toMap(SupplierApiDetailVO::getSupplierId, supplierApiDetailVO -> supplierApiDetailVO));
        // 查询仓库信息
        WarehouseSearchDTO searchDTO = new WarehouseSearchDTO();
        searchDTO.setWarehouseIds(new ArrayList<>(warehouseIds));
        List<WarehouseVO> warehouses = warehouseFeignClient.listWarehouse(searchDTO);
        Map<Long, String> warehouseMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(warehouses)) {
            warehouseMap = warehouses.stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
        }
        // 补充信息
        for (EsPurchaseOrderVO purchaseOrderVO : list) {
            SupplierApiDetailVO supplierApiDetailVO = supplierMap.get(purchaseOrderVO.getSupplierId());
            purchaseOrderVO.setSupplierName(supplierApiDetailVO.getSupplierName());
            purchaseOrderVO.setContactPhone(supplierApiDetailVO.getContactPhone());
            purchaseOrderVO.setWarehouseName(warehouseMap.get(purchaseOrderVO.getWarehouseId()));
            purchaseOrderVO.setStockPointType(purchaseOrderVO.getStockPointType() == null ? StockPointType.WAREHOUSE.getValue() : purchaseOrderVO.getStockPointType());
        }
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param param 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsPurchaseOrderVO> pageSearchResult(PurchaseOrderSearchDTO param) {
        if (Objects.isNull(param.getPageSize())) {
            param.setPageSize(10);
        }
        if (Objects.isNull(param.getPageNum())) {
            param.setPageNum(1);
        }
        //1、查询订单数据
        SearchResponse response = searchResult(param);
        //2.处理响应的数据
        return pageEsOrderBO(response, param.getPageSize());
    }

    private EsPageVO<EsPurchaseOrderVO> pageEsOrderBO(SearchResponse response, Integer pageSize) {
        EsPageVO<EsPurchaseOrderVO> pageVO = new EsPageVO<>();
        List<EsPurchaseOrderVO> purchaseOrderList = getOrderRefundListByResponse(response.getHits().getHits());
        pageVO.setList(purchaseOrderList);
        buildSearchPage(pageVO, response, pageSize);
        return pageVO;
    }

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

    private SearchResponse searchResult(PurchaseOrderSearchDTO param) {
        // 封装检索条件
        SearchRequest searchRequest = buildSearchRequest(param);
        SearchResponse response;
        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("搜索返回结果：{}", response.toString());

        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsPurchaseOrderVO> getOrderRefundListByResponse(SearchHit[] hits) {
        List<EsPurchaseOrderVO> purchaseOrderList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Integer lang = I18nMessage.getLang();
        for (SearchHit hit : hits) {
            EsPurchaseOrderBO purchaseOrder = Json.parseObject(hit.getSourceAsString(), EsPurchaseOrderBO.class);
            if (!CollectionUtils.isEmpty(purchaseOrder.getPurchaseOrderItems())) {
                for (EsPurchaseOrderItemBO purchaseOrderItemBO : purchaseOrder.getPurchaseOrderItems()) {
                    if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                        purchaseOrderItemBO.setSpuName(purchaseOrderItemBO.getSpuNameEn());
                        purchaseOrderItemBO.setSkuName(purchaseOrderItemBO.getSkuNameEn());
                    }
                    if (StrUtil.isBlank(purchaseOrderItemBO.getSpuName())) {
                        purchaseOrderItemBO.setSpuName(purchaseOrderItemBO.getSpuNameZh());
                    }
                    if (StrUtil.isBlank(purchaseOrderItemBO.getSkuName())) {
                        purchaseOrderItemBO.setSkuName(purchaseOrderItemBO.getSkuNameZh());
                    }
                }
            }
            if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                purchaseOrder.setSpuName(purchaseOrder.getSpuNameEn());
            }
            if (StrUtil.isBlank(purchaseOrder.getSpuName())) {
                purchaseOrder.setSpuName(purchaseOrder.getSpuNameZh());
            }
            purchaseOrderList.add(BeanUtil.map(purchaseOrder, EsPurchaseOrderVO.class));
        }
        return purchaseOrderList;
    }


    /**
     * 准备检索请求
     *
     * @param param 搜索参数
     * @return
     */
    private SearchRequest buildSearchRequest(PurchaseOrderSearchDTO param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索
        keywordSearch(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        if (Objects.nonNull(param.getPageNum()) && Objects.nonNull(param.getPageSize())) {
            //分页
            searchSourceBuilder.from((param.getPageNum() - 1) * param.getPageSize());
            searchSourceBuilder.size(param.getPageSize());
        }

        log.info("构建的DSL语句 {}", searchSourceBuilder);

        return new SearchRequest(new String[]{EsIndexEnum.PURCHASE_ORDER.value()}, searchSourceBuilder);
    }


    /**
     * 关键字搜索
     */
    private void keywordSearch(PurchaseOrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {

        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可


        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();

        // 店铺名称
        if (StrUtil.isNotBlank(param.getShopName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("shopName", param.getShopName()));
        }

        // 供应商名称
        if (StrUtil.isNotBlank(param.getSupplierName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("supplierName", param.getSupplierName()));
        }

        // 商品名称
        if (StrUtil.isNotBlank(param.getSpuName())) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            boolQuery.should(QueryBuilders.matchQuery("purchaseOrderItems.spuNameZh", param.getSpuName()));
            boolQuery.should(QueryBuilders.matchQuery("purchaseOrderItems.spuNameEn", param.getSpuName()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("purchaseOrderItems", boolQuery, ScoreMode.None);
            boolQueryBuilder.must(nestedQueryBuilder);
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     *
     * @param param            查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(PurchaseOrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {
        // 店铺id
        if (Objects.nonNull(param.getShopId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId()));
        }
        // 店铺id列表
        if (CollUtil.isNotEmpty(param.getShopIds())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopIds()));
        }
        // 供应商id
        if (Objects.nonNull(param.getSupplierId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
        }
        // 供应商id列表
        if (CollUtil.isNotEmpty(param.getSupplierIds())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
        }
        // 供应商id列表
        if (Objects.nonNull(param.getPurchaseOrderId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("purchaseOrderId", param.getPurchaseOrderId()));
        }
        // 联系人
        if (StrUtil.isNotBlank(param.getConsignee())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("consignee", param.getConsignee()));
        }
        // 手机号
        if (StrUtil.isNotBlank(param.getMobile())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mobile", param.getMobile()));
        }

        // 创建时间:  开始时间 - 结束时间
        if (param.getStartTime() != null || param.getEndTime() != null) {
            String createTime = "createTime";
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime);
            if (param.getStartTime() != null) {
                rangeQueryBuilder.gte(param.getStartTime());
            }
            if (param.getEndTime() != null) {
                rangeQueryBuilder.lte(param.getEndTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 状态 0:已作废 1:待入库 2:已发货 3.部分入库 5:已完成
        if (!CollectionUtils.isEmpty(param.getStatuses())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("status", param.getStatuses()));
        } else if (Objects.nonNull(param.getStatus())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("status", param.getStatus()));
        }

        // sku编码
        if (StrUtil.isNotBlank(param.getPartyCodes())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("partyCode", param.getPartyCodes()));
        }

        // 商品编码
        if (StrUtil.isNotBlank(param.getSpuCodes())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("spuCode", param.getSpuCodes()));
        }
    }
}
