package com.yeejoin.amos.boot.module.jg.biz.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.module.jg.api.dto.JgEquipInsuranceDto;
import com.yeejoin.amos.boot.module.jg.api.entity.JgEquipInsurance;
import com.yeejoin.amos.boot.module.jg.api.mapper.CommonMapper;
import com.yeejoin.amos.boot.module.jg.api.mapper.JgEquipInsuranceMapper;
import com.yeejoin.amos.boot.module.jg.api.service.IJgEquipInsuranceService;
import com.yeejoin.amos.boot.module.jg.api.vo.SortVo;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections.CollectionUtils;
import org.apache.lucene.queryparser.classic.QueryParser;
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.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.filter.Filters;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.rdbms.service.BaseService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author LiuLin
 * @date 2024-10-17
 */
@Service
public class JgEquipInsuranceServiceImpl extends BaseService<JgEquipInsurance,JgEquipInsurance,JgEquipInsuranceMapper> implements IJgEquipInsuranceService {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private CommonServiceImpl commonServiceImpl;

    private static final String SECOND = "second";

    private static final String DEVICE_LIST = "deviceList";

    private static final String INDEX_NAME = "idx_biz_view_jg_all";

    public static final String REGION_CODE = "regionCode";
    public static final String REGION_NAME = "regionName";
    public static final String ELEVATOR = "elevator";
    public static final String CYLINDER = "cylinder";
    public static final String ELEVATOR_INSURANCE_COUNT = "elevatorInsuranceCount";
    public static final String CYLINDER_INSURANCE_COUNT = "cylinderInsuranceCount";
    public static final String ELEVATOR_INSURANCE_RATE = "elevatorInsuranceRate";
    public static final String CYLINDER_INSURANCE_RATE = "cylinderInsuranceRate";

    private static final Map<String, String> regionCodeOrgCodeMap = new ConcurrentHashMap<>();

    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Autowired
    CommonMapper commonMapper;

    /**
     * 分页查询
     */
    public Page<JgEquipInsuranceDto> queryForJgEquipInsurancePage(Page<JgEquipInsurance> page,
                                                               @ApiParam(value = "排序字段") @RequestParam(value = "sort", required = false) String sort,
                                                               JgEquipInsuranceDto dto)   {
        SortVo sortMap = commonServiceImpl.sortFieldConversion(sort);
        return this.baseMapper.getListPage(page, sortMap, dto);
    }

    /**
     * 列表查询 示例
     */
    public List<JgEquipInsurance> queryForJgEquipInsuranceList()   {
        return this.queryForList("" , false);
    }

    @Transactional(rollbackFor = Exception.class)
    public Object save(JSONObject map) {
        // 从 Redis 中获取 ReginParams 对象
        ReginParams reginParams = JSONObject.parseObject(
                redisUtils.get(RedisKey.buildReginKey(RequestContext.getExeUserId(), RequestContext.getToken())).toString(),
                ReginParams.class
        );

        // 解析 secondMap 和设备保险信息
        JSONObject secondMap = map.getJSONObject("second");
        JgEquipInsurance equipInsurance = map.getObject(SECOND, JgEquipInsurance.class);

        // 获取设备列表并进行校验
        List<Map> deviceList = secondMap.getJSONArray(DEVICE_LIST).toJavaList(Map.class);
        if (CollectionUtils.isEmpty(deviceList)) {
            throw new BadRequest("请选择设备信息!");
        }

        // 使用流创建新的保险对象列表并保存
        return this.saveBatch(
                deviceList.stream()
                        .map(x -> {
                            JgEquipInsurance newInsurance = new JgEquipInsurance();
                            BeanUtils.copyProperties(equipInsurance, newInsurance);
                            newInsurance.setEquId(String.valueOf(x.get("record")));
                            newInsurance.setEquList(String.valueOf(x.get("EQU_LIST_CODE")));
                            newInsurance.setEquCategory(String.valueOf(x.get("EQU_CATEGORY_CODE")));
                            newInsurance.setEquDefine(String.valueOf(x.get("EQU_DEFINE_CODE")));
                            newInsurance.setRecUserId(reginParams.getUserModel().getUserId());
                            newInsurance.setRecUserName(reginParams.getUserModel().getUserName());
                            newInsurance.setRecDate(new Date());
                            newInsurance.setIsDelete(false);
                            return newInsurance;
                        })
                        .collect(Collectors.toList())
        );
    }

    public JgEquipInsuranceDto getDetail(Long sequenceNbr) {
        return this.getBaseMapper().getDetailById(sequenceNbr);
    }

    public Map<String, Object> countLiabilityInsuranceStatisticsByCity() {
        List<RegionModel> regionModelList = Systemctl.regionClient.queryByLevel("2").getResult();
        List<Map<String, Object>> esQueryResults = processAsync(regionModelList, this::esQuery);
        return assemblingHistogramData(esQueryResults);
    }

    // 公共方法，用于异步处理逻辑
    private List<Map<String, Object>> processAsync(List<RegionModel> regionModelList, Function<RegionModel, Map<String, Object>> function) {
        return regionModelList.stream()
                .map(regionModel -> CompletableFuture.supplyAsync(() -> function.apply(regionModel)))
                .collect(Collectors.toList())
                .stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }

    public String getAndSetOrgCode(String cityCode) {
        String orgCode = regionCodeOrgCodeMap.get(cityCode);
        if (orgCode == null) {
            orgCode = commonMapper.getOrgCodeByCompanyCode(cityCode);
            if (orgCode != null) {
                regionCodeOrgCodeMap.put(cityCode, orgCode);
            }
        }
        return orgCode;
    }

    private Map<String, Object> esQuery(RegionModel regionModel) {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        String orgCode = this.getAndSetOrgCode(String.valueOf(regionModel.getRegionCode()));
        boolQueryBuilder.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 有监管码
        boolQueryBuilder.must(QueryBuilders.existsQuery("SUPERVISORY_CODE"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("SUPERVISORY_CODE","null"));

        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.aggregation(
                AggregationBuilders.filters("equipmentCount",
                        new FiltersAggregator.KeyedFilter("电梯",
                                QueryBuilders.termQuery("EQU_LIST_CODE", "3000")
                        ),
                        new FiltersAggregator.KeyedFilter("气瓶",
                                QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300")
                        )
                )
        );

        searchRequest.source(searchSourceBuilder);
        try {
            // 执行搜索请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 获取所有聚合结果
            Filters aggregation = searchResponse.getAggregations().get("equipmentCount");
            List<Map<String, Object>> equipInsuranceMap = this.baseMapper.getEquipInsuranceTotal(orgCode);
            return getStringObjectMap(regionModel, aggregation, equipInsuranceMap);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyMap();
    }

    private static Map<String, Object> getStringObjectMap(RegionModel regionModel, Filters aggregation, List<Map<String, Object>> equipInsuranceMap) {
        long elevatorCount = 0;
        long cylinderCount = 0;
        BigDecimal elevatorInsuranceRate;
        BigDecimal cylinderInsuranceRate;

        // 遍历 bucket 获取电梯和气瓶的总数
        for (Filters.Bucket bucket : aggregation.getBuckets()) {
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            if ("电梯".equals(key)) {
                elevatorCount = docCount;
            } else if ("气瓶".equals(key)) {
                cylinderCount = docCount;
            }
        }

        // 将Long类型的值转换为BigDecimal
        BigDecimal cylinderInsuranceCount = BigDecimal.valueOf((Long) equipInsuranceMap.get(0).get(CYLINDER_INSURANCE_COUNT));
        BigDecimal elevatorInsuranceCount = BigDecimal.valueOf((Long) equipInsuranceMap.get(0).get(ELEVATOR_INSURANCE_COUNT));

        // 计算保险覆盖率，避免除以零的情况
        elevatorInsuranceRate = elevatorCount == 0 ? BigDecimal.ZERO :
                elevatorInsuranceCount.divide(BigDecimal.valueOf(elevatorCount), 2, RoundingMode.HALF_UP);
        cylinderInsuranceRate = cylinderCount == 0 ? BigDecimal.ZERO :
                cylinderInsuranceCount.divide(BigDecimal.valueOf(cylinderCount), 2, RoundingMode.HALF_UP);

        // 构建结果Map
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put(REGION_CODE, regionModel.getRegionCode());
        resultMap.put(REGION_NAME, regionModel.getRegionName());
        resultMap.put(ELEVATOR, elevatorCount);
        resultMap.put(CYLINDER, cylinderCount);
        resultMap.put(ELEVATOR_INSURANCE_RATE, elevatorInsuranceRate);
        resultMap.put(CYLINDER_INSURANCE_RATE, cylinderInsuranceRate);
        return resultMap;
    }

    private static Map<String, Object> assemblingHistogramData(List<Map<String, Object>> esQueryResults) {
        // 组装数据
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> barSeriesData = new ArrayList<>();
        List<Map<String, Object>> lineSeriesData = new ArrayList<>();
        List<String> axisData = new ArrayList<>();
        List<String> cylinderDataList = new ArrayList<>();
        List<String> elevatorDataList = new ArrayList<>();
        List<String> cylinderDataListRate = new ArrayList<>();
        List<String> elevatorDataListRate = new ArrayList<>();
        esQueryResults.forEach(x -> {
            axisData.add(String.valueOf(x.get(REGION_NAME)));
            cylinderDataList.add(String.valueOf(x.get(CYLINDER)));
            elevatorDataList.add(String.valueOf(x.get(ELEVATOR)));
            elevatorDataListRate.add(String.valueOf(x.get(ELEVATOR_INSURANCE_RATE)));
            cylinderDataListRate.add(String.valueOf(x.get(CYLINDER_INSURANCE_RATE)));
        });

        HashMap<String, Object> temMap1 = new HashMap<>();
        temMap1.put("data", elevatorDataList);
        temMap1.put("name", "电梯");
        temMap1.put("type", "bar");
        HashMap<String, Object> temMap2 = new HashMap<>();
        temMap2.put("data", cylinderDataList);
        temMap2.put("name", "气瓶");
        temMap2.put("type", "bar");
        HashMap<String, Object> temMap3 = new HashMap<>();
        temMap3.put("data", elevatorDataListRate);
        temMap3.put("name", "电梯覆盖率");
        temMap3.put("type", "line");
        HashMap<String, Object> temMap4 = new HashMap<>();
        temMap4.put("data", cylinderDataListRate);
        temMap4.put("name", "气瓶覆盖率");
        temMap4.put("type", "line");
        barSeriesData.add(temMap1);
        barSeriesData.add(temMap2);
        lineSeriesData.add(temMap3);
        lineSeriesData.add(temMap4);
        result.put("axisData", axisData);
        result.put("barSeriesData", barSeriesData);
        result.put("lineSeriesData", lineSeriesData);
        return result;
    }
}