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

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.dto.CountDto;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.module.common.api.dto.CylinderFillingRecordStatisticsDto;
import com.yeejoin.amos.boot.module.common.api.dto.DPFilterParamDto;
import com.yeejoin.amos.boot.module.common.api.dto.DPFilterParamForDetailDto;
import com.yeejoin.amos.boot.module.jg.api.enums.CylinderTypeEnum;
import com.yeejoin.amos.boot.module.statistics.api.dto.CylinderFillingMessageModel;
import com.yeejoin.amos.boot.module.statistics.api.dto.SecurityIndexCountItemDto;
import com.yeejoin.amos.boot.module.statistics.api.dto.SubTreeDto;
import com.yeejoin.amos.boot.module.statistics.api.enums.AnomalyTypeEnum;
import com.yeejoin.amos.boot.module.statistics.api.mapper.AQZSDPStatisticsMapper;
import com.yeejoin.amos.boot.module.statistics.api.mapper.CylinderStatisticsMapper;
import com.yeejoin.amos.boot.module.ymt.api.dto.TzBaseEnterpriseInfoDto;
import com.yeejoin.amos.boot.module.ymt.api.entity.ESCylinderFillingRecordDto;
import com.yeejoin.amos.boot.module.ymt.api.entity.PageParam;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzBaseEnterpriseInfo;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseEnterpriseInfoMapper;
import com.yeejoin.amos.boot.module.ymt.flc.api.mapper.CylinderAreaDataMapper;
import com.yeejoin.amos.boot.module.ymt.flc.api.mapper.CylinderInfoMapper;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.CompanyModel;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
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.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 气瓶区域统计表服务实现类
 *
 * @author system_generator
 * @date 2022-03-08
 */
@Service
public class CylinderDPStatisticsServiceImpl {

    private StCommonServiceImpl stCommonService;

    private RestHighLevelClient restHighLevelClient;

    private TzBaseEnterpriseInfoMapper baseEnterpriseInfoMapper;

    private CylinderAreaDataMapper cylinderAreaDataMapper;

    private CylinderStatisticsMapper cylinderStatisticsMapper;

    private CylinderInfoMapper cylinderInfoMapper;

    private AQZSDPStatisticsMapper statisticsMapper;

    @Autowired
    private AQZSDPStatisticsServiceImpl statisticsService;

    private final TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper;

    private final CylinderOpenApiServiceImpl cylinderOpenApiService;

    /**
     * 气瓶图列换算单位上限
     */
    private final static Long CYLINDER_LEGEND_UPPER_LIMIT = 100000L;

    public CylinderDPStatisticsServiceImpl(StCommonServiceImpl stCommonService, RestHighLevelClient restHighLevelClient, TzBaseEnterpriseInfoMapper baseEnterpriseInfoMapper, CylinderAreaDataMapper cylinderAreaDataMapper, CylinderStatisticsMapper cylinderStatisticsMapper, CylinderInfoMapper cylinderInfoMapper, AQZSDPStatisticsMapper statisticsMapper, TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper, CylinderOpenApiServiceImpl cylinderOpenApiService) {
        this.stCommonService = stCommonService;
        this.restHighLevelClient = restHighLevelClient;
        this.baseEnterpriseInfoMapper = baseEnterpriseInfoMapper;
        this.cylinderAreaDataMapper = cylinderAreaDataMapper;
        this.cylinderStatisticsMapper = cylinderStatisticsMapper;
        this.cylinderInfoMapper = cylinderInfoMapper;
        this.statisticsMapper = statisticsMapper;
        this.tzBaseEnterpriseInfoMapper = tzBaseEnterpriseInfoMapper;
        this.cylinderOpenApiService = cylinderOpenApiService;
    }


    public List<Map<String, Object>> getCylinderStatisticsData(String regionCode) throws IOException {
        // 构建搜索请求
        String orgCode = stCommonService.getAndSetOrgCode(regionCode);
        if (StringUtils.isEmpty(orgCode)) {
            return new ArrayList<>();
        }
        SearchRequest searchRequest = new SearchRequest("idx_biz_view_jg_all");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建查询条件
        searchSourceBuilder.query(
                QueryBuilders.boolQuery()
                        .must(QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300"))
                        // 只统计已纳管设备
                        .must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", Boolean.TRUE))
                        // 按照管辖机构区域信息模糊查询
                        .must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"))
        );
        TermsAggregationBuilder aggregation = AggregationBuilders.terms("USE_UNIT_CREDIT_CODE").field("USE_UNIT_CREDIT_CODE.keyword").size(10);
        searchSourceBuilder.aggregation(aggregation);
        searchRequest.source(searchSourceBuilder);
        // 执行搜索并获取响应
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 处理聚合结果
        // 这里只是打印出聚合的名称和桶的数量，你需要根据自己的需求来处理这些数据
        Terms terms = searchResponse.getAggregations().get("USE_UNIT_CREDIT_CODE");
        List<Map<String, Object>> dataList = new ArrayList<>();
        Set<String> keys = new HashSet<>();
        for (Terms.Bucket bucket : terms.getBuckets()) {
            Map<String, Object> item = new HashMap<>();
            item.put("count", bucket.getDocCount());
            item.put("key", bucket.getKeyAsString());
            dataList.add(item);
            keys.add(bucket.getKeyAsString());
        }
        Map<String, TzBaseEnterpriseInfo> useCodeEntryMap = new HashMap<>();
        if(keys.size()  > 0){
            useCodeEntryMap = getStringTzBaseEnterpriseInfoDtoMap(keys);
        }
        for (Map<String, Object> item : dataList) {
            Object key = item.get("key");
            if (useCodeEntryMap.containsKey(key)) {
                item.put("name", useCodeEntryMap.get(key).getUseUnit());
                item.put("city", useCodeEntryMap.get(key).getCity());
                item.put("province", useCodeEntryMap.get(key).getProvince());
                item.put("district", useCodeEntryMap.get(key).getDistrict());
            }
        }
        return dataList;
    }

    private Map<String, TzBaseEnterpriseInfo> getStringTzBaseEnterpriseInfoDtoMap(Set<String> keys) {
        LambdaQueryWrapper<TzBaseEnterpriseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(TzBaseEnterpriseInfo::getUseUnitCode, keys);
        queryWrapper.select(TzBaseEnterpriseInfo::getUseUnitCode, TzBaseEnterpriseInfo::getProvince, TzBaseEnterpriseInfo::getCity, TzBaseEnterpriseInfo::getDistrict, TzBaseEnterpriseInfo::getUseUnit);
        List<TzBaseEnterpriseInfo> baseEnterpriseInfos = baseEnterpriseInfoMapper.selectList(queryWrapper);
        return baseEnterpriseInfos.stream().collect(Collectors.toMap(TzBaseEnterpriseInfo::getUseUnitCode, f -> f));
    }


    public Map<String, Object> getCylinderStatisticsDataByCity(DPFilterParamDto dpFilterParamDto) {
        String regionCode = dpFilterParamDto.getCityCode();
        List<RegionModel> regionList = stCommonService.setRegionIfRootParentAndNoAccessIf3Level(regionCode);
        List<Map<String, Object>> legendData = new ArrayList<>();
        List<String> xdata = this.buildXData(regionList);
        List<Long> qiping = getYDataForQP(regionList);
        Long totalCyNum = qiping.stream().mapToLong(e -> e).sum();
        List<Long> qizhan = getYDataForQZ(regionList);
        Map<String, Object> qiLegend = getQPLegend(totalCyNum);
        Map<String, Object> zhanLegend = getQZLegend("气站数量", "qizhan");
        legendData.add(qiLegend);
        legendData.add(zhanLegend);
        Map<String, Object> result = new HashMap<>();
        result.put("qizhan", qizhan);
        result.put("qiping", this.changeNum2Million(totalCyNum, qiping));
        result.put("xdata", xdata);
        result.put("legendData", legendData);
        return result;
    }

    private List changeNum2Million(Long totalCyNum, List<Long> qiping) {
        if (totalCyNum > CYLINDER_LEGEND_UPPER_LIMIT) {
            return qiping.stream().map(num -> {
                BigDecimal numDecimal = new BigDecimal(num.toString());
                BigDecimal millionNum = numDecimal.divide(new BigDecimal("10000"), 4, RoundingMode.HALF_UP);
                return millionNum.toPlainString();
            }).collect(Collectors.toList());
        } else {
            return qiping;
        }
    }

    private Map<String, Object> getQPLegend(Long totalCyNum) {
        Map<String, Object> qiLegend = new HashMap<>();
        if (totalCyNum > CYLINDER_LEGEND_UPPER_LIMIT) {
            qiLegend.put("value", "气瓶数量(万)");
        } else {
            qiLegend.put("value", "气瓶数量");
        }
        qiLegend.put("dataKey", "qiping");
        return qiLegend;
    }

    private Map<String, Object> getQZLegend(String label, String key) {
        Map<String, Object> qiLegend = new HashMap<>();
        qiLegend.put("value", label);
        qiLegend.put("dataKey", key);
        return qiLegend;
    }

    private List<Long> getYDataForQZ(List<RegionModel> regionList) {
        List<CountDto> enterpriseCountList = regionList.parallelStream().map(e -> {
            CountDto dto = new CountDto();
            dto.setKeyStr(e.getRegionCode() + "");
            String orgCode = stCommonService.getAndSetOrgCode(e.getRegionCode() + "");
            if (StringUtils.isNotEmpty(orgCode)) {
                Long enterpriseNum = cylinderStatisticsMapper.countEnterpriseNumForCylinder(orgCode);
                dto.setLongValue(enterpriseNum);
            } else {
                dto.setLongValue(0L);
            }
            return dto;
        }).collect(Collectors.toList());
        return regionList.stream().map(r -> enterpriseCountList.stream().filter(e -> e.getKeyStr().equals(r.getRegionCode() + "")).mapToLong(CountDto::getLongValue).sum()).collect(Collectors.toList());
    }

    private List<Long> getYDataForQP(List<RegionModel> regionList) {
        List<Long> qiping = new ArrayList<>();
        regionList.forEach(r -> {
            String orgCode = stCommonService.getAndSetOrgCode(r.getRegionCode() + "");
            if (StringUtils.isNotEmpty(orgCode)) {
                Long num = this.countForCylinderNum(orgCode);
                qiping.add(num);
            } else {
                qiping.add(0L);
            }
        });
        return qiping;
    }

    private Long countForCylinderNum(String orgCode) {
        long num = 0;
        CountRequest request = new CountRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        boolMust.must(QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300"));
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 只统计已纳管设备
        boolMust.must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", Boolean.TRUE));
        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return num;
    }

    private Long countForCylinderNumForVehicleUsed(String orgCode) {
        long num = 0;
        CountRequest request = new CountRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.termQuery("WHETHER_VEHICLE_CYLINDER.keyword", "1"));
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 只统计已纳管设备
        boolMust.must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", Boolean.TRUE));
        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return num;
    }

    private List<String> buildXData(List<RegionModel> regionList) {
        return regionList.stream().map(RegionModel::getRegionName).collect(Collectors.toList());
    }

    public Map<String, Object> getCylinderStatisticsDataByCityForTotal(DPFilterParamDto dpFilterParamDto) {
        Map<String, Object> result = new HashMap<>();
        String orgCode = stCommonService.getAndSetOrgCode(dpFilterParamDto.getCityCode());
        getCylinderMapCount(orgCode, result);
        return result;
    }

    private Map<String, Object> getCylinderMapCount(String orgCode, Map<String, Object> result) {
        if (StringUtils.isNotEmpty(orgCode)) {
            Long cylindersCount = this.countForCylinderNum(orgCode);
            Long automotiveGasCount = this.countForCylinderNumForVehicleUsed(orgCode);
            result.put("cylindersCount", cylindersCount);
            result.put("stationCount", cylinderStatisticsMapper.countEnterpriseNumForCylinder(orgCode));
            result.put("operatorCount", cylinderAreaDataMapper.getOpertorStatisticsDataByCity(Collections.singletonList(orgCode)));
            // 液化气瓶 (个)
            result.put("liquefiedGasCount", cylindersCount - automotiveGasCount);
            // 车用气瓶 (个)
            result.put("automotiveGasCount", automotiveGasCount);
            // 工业气瓶 (个)
            result.put("industrialGasCount", 0L);
            // 使用登记数量
            result.put("useRegistrationQuantityCount", this.countForCylinderCertNum(orgCode));
            // 检验临期气瓶数
            result.put("jylqsbCount", this.countForCylinderTemporaryInspect(orgCode));
            // 检验超期气瓶数
            result.put("jycqsbCount", this.countForCylinderOverdueInspect(orgCode));
            // 充气量
            Long fillingVolumeCount = cylinderStatisticsMapper.countFillingVolumeCount(orgCode);
            result.put("fillingVolumeCount", fillingVolumeCount == null ? 0L : fillingVolumeCount);
            // 卸液量
            Long dischargeVolumeCount = cylinderStatisticsMapper.countDischargeVolumeCount(orgCode);
            result.put("dischargeVolumeCount", dischargeVolumeCount == null ? 0L : dischargeVolumeCount);
        } else {
            this.setDefaultValueIfNoData(result, "cylindersCount", "stationCount", "operatorCount", "liquefiedGasCount", "automotiveGasCount", "industrialGasCount", "useRegistrationQuantityCount", "jylqsbCount", "jycqsbCount","fillingVolumeCount","dischargeVolumeCount");
        }
        return result;
    }

    /**
     * 临期检验设备数统计
     *
     * @param orgCode 过滤条件
     * @return 临期设备的数量
     */
    private Long countForCylinderTemporaryInspect(String orgCode) {
        long num;
        CountRequest request = new CountRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        boolMust.must(QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300"));
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 只统计已纳管设备
        boolMust.must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", Boolean.TRUE));
        // 且下次检验日期大于等于当天
        long currentDayTime = DateUtil.parse(DateUtil.today(), "yyy-MM-dd").getTime();
        // 且查询 下次检验日期 <= 当前天+30天 极为临期
        long currentDayAfter30DayTime = DateUtil.offsetDay(DateUtil.parse(DateUtil.today(), "yyy-MM-dd"), 30).getTime();
        boolMust.must(QueryBuilders.rangeQuery("NEXT_INSPECT_DATE").gte(currentDayTime).lte(currentDayAfter30DayTime));
        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return num;
    }


    /**
     * 超期检验设备数统计
     *
     * @param orgCode 过滤条件
     * @return 超期设备的数量
     */
    private Long countForCylinderOverdueInspect(String orgCode) {
        long num = 0;
        CountRequest request = new CountRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        // 设备类别2300气瓶
        boolMust.must(QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300"));
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 只统计已纳管设备
        boolMust.must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", Boolean.TRUE));
        // 查询下次检验日期小于当前天的设备，即为超期检验超期设备
        long currentDayTime = DateUtil.parse(DateUtil.now(), "yyy-MM-dd").getTime();
        boolMust.must(QueryBuilders.rangeQuery("NEXT_INSPECT_DATE").lt(currentDayTime));
        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return num;
    }

    private Long countForCylinderCertNum(String orgCode) {
        return cylinderStatisticsMapper.countForCylinderCertNum(orgCode);
    }

    public List<Map<String, Object>> getCylinderStatisticsDataByCityForMap(DPFilterParamDto dpFilterParamDto) throws Exception {
        List<RegionModel> regionList = stCommonService.setRegionIfRootParent(dpFilterParamDto.getCityCode());
        return regionList.parallelStream().map(r -> {
            String orgCode = stCommonService.getAndSetOrgCode(r.getRegionCode().toString());
            Map<String, Object> item = new HashMap<>();
            item.put("regionCode", r.getRegionCode());
            item.put("regionName", r.getRegionName());
            getCylinderMapCount(orgCode, item);
            return item;
        }).collect(Collectors.toList());
    }

    private void setDefaultValueIfNoData(Map<String, Object> item, String... fields) {
        for (String field : fields) {
            item.put(field, 0L);
        }
    }

    public List<CylinderFillingRecordStatisticsDto> fillingTimesAndQuantity(String reginCode) {
        String orgCode = stCommonService.getAndSetOrgCode(reginCode);
        List<String> times = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            times.add(LocalDate.now().minusDays(i).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        }
        List<CylinderFillingRecordStatisticsDto> fillingRecord = new ArrayList<>(0);
        List<CylinderFillingRecordStatisticsDto> offloading = new ArrayList<>(0);
        if(orgCode != null){
           fillingRecord = cylinderInfoMapper.queryFillingRecordByOrgCode(orgCode, LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),null);
           offloading = cylinderInfoMapper.queryoffloadingByOrgCode(orgCode, LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),null);
        }
        Map<String, String> fillingRecordMap = fillingRecord.stream().collect(Collectors.toMap(CylinderFillingRecordStatisticsDto::getTime, CylinderFillingRecordStatisticsDto::getFillingQuantity));
        Map<String, String> offloadingMap = offloading.stream().collect(Collectors.toMap(CylinderFillingRecordStatisticsDto::getTime, CylinderFillingRecordStatisticsDto::getOffloadingVolume));
        List<CylinderFillingRecordStatisticsDto> data = new ArrayList<>();
        for (String key : times) {
            CylinderFillingRecordStatisticsDto dayData = new CylinderFillingRecordStatisticsDto();
            dayData.setTime(key);
            dayData.setFillingQuantity(fillingRecordMap.getOrDefault(key, "0"));
            dayData.setOffloadingVolume(offloadingMap.getOrDefault(key, "0"));
            data.add(dayData);
        }
        return data;
    }


    public List<Map<String, Object>> cylinderIssueMonthList(String regionCode) {
        String orgCode = stCommonService.getAndSetOrgCode(regionCode);
        if(orgCode == null){
            return new ArrayList<>();
        }
        String time = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        return statisticsMapper.cylinderIssueMonthList(orgCode, time);
    }

    public List<Map<String, Object>> stationRate(DPFilterParamDto dpFilterParamDto) {
        List<RegionModel> regionList = stCommonService.setRegionIfRootParent(dpFilterParamDto.getCityCode());
        List<Map<String,Object>> returnList = regionList.parallelStream().map(r -> {
            String orgCode = stCommonService.getAndSetOrgCode(r.getRegionCode().toString());
            Map<String, Object> item = new HashMap<>();
            item.put("regionCode", r.getRegionCode());
            item.put("regionName", r.getRegionName());
            getStationRate(orgCode, item);
            return item;
        }).collect(Collectors.toList());
        Collections.sort(returnList, (o1, o2) -> ((Double)o2.get("stationRate")).compareTo((Double)o1.get("stationRate")));
        for(int i = 0;i<returnList.size();i++){
            returnList.get(i).put("sequenceNbr",i);
        }
        return returnList;
    }

    private Map<String,Object> getStationRate(String orgCode,Map<String,Object> result){
        // 气站总数
        Long totalNum = null == orgCode ? null : cylinderStatisticsMapper.countEnterpriseNumForCylinder(orgCode);
        // 已对接总数(存在充装业务数据的企业则认为已对接)
        Long count = null == orgCode ? null : cylinderStatisticsMapper.countEnterpriseUsed(orgCode);

        if (totalNum != null && count != null && totalNum > 0 ) {
            BigDecimal percent = (new BigDecimal(count.doubleValue() * 100).divide(new BigDecimal(totalNum.doubleValue()), 2, RoundingMode.HALF_UP));
            result.put("stationRate", Double.valueOf(percent.toString()));
        } else {
            result.put("stationRate", 0.0);
        }
        result.put("totalNum", totalNum);
        result.put("count", count);
        return result;
    }

    public List<SubTreeDto> securityIndexSubTree() {
        List<SubTreeDto> list = new ArrayList<>();
        SubTreeDto subTreeDto = new SubTreeDto();
        subTreeDto.setValue(CylinderTypeEnum.CYLINDER.getCode());
        subTreeDto.setTitle("气" + CylinderTypeEnum.CYLINDER.getName());
        list.add(subTreeDto);
        return list;
    }

    public Map<String, Object> securityIndexSubChart(DPFilterParamDto dpFilterParamDto) {
        List<RegionModel> regionModels = stCommonService.setRegionIfRootParentAndNoAccessIf3Level(dpFilterParamDto.getCityCode());
        Map<String, SecurityIndexCountItemDto> regionCodeSecurityIndexMap = statisticsService.getSecurityIndexCountItemDtoMap(regionModels);
        List<Map<String, String>> finalData = new ArrayList<>();
        statisticsService.getCitySecurityIndex(finalData, regionCodeSecurityIndexMap);
        List<String> xdata = stCommonService.buildXData(regionModels);
        List<String> yData = new ArrayList<>();
        regionModels.forEach(r -> {
            String s = finalData.stream().filter(e -> e.get("regionCode").equals(r.getRegionCode().toString())).collect(Collectors.toList()).get(0).get("value");
            yData.add(s);
        });
        Map<String, Object> result = new HashMap<>();
        result.put("xdata", xdata);
        result.put("securityIndex", yData);
        List<Map<String, Object>> legendData = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("dataKey", "securityIndex");
        map.put("value", "各地市区域安全指数");
        map.put("chartType", "bar");
        legendData.add(map);
        result.put("legendData", legendData);
        return result;
    }

    public List<SubTreeDto> getFillingSubTree() {
        List<SubTreeDto> list = new ArrayList<>();
        SubTreeDto subTreeDto = new SubTreeDto();
        subTreeDto.setTitle("气" + CylinderTypeEnum.CYLINDER.getName());
        subTreeDto.setValue(CylinderTypeEnum.CYLINDER.getCode());
        List<SubTreeDto> children = new ArrayList<>();
        for (CylinderTypeEnum value : CylinderTypeEnum.values()) {
            if (value.getCode() != CylinderTypeEnum.CYLINDER.getCode()) {
                SubTreeDto subTree = new SubTreeDto();
                subTree.setTitle(value.getName());
                subTree.setValue(value.getCode());
                children.add(subTree);
            }
        }
        subTreeDto.setChildren(children);
        list.add(subTreeDto);
        return list;
    }

    public Map<String, Object> getFillingSubChart(DPFilterParamForDetailDto dpFilterParamForDetailDto) {
        List<RegionModel> regionModels = stCommonService.setRegionIfRootParentAndNoAccessIf3Level(dpFilterParamForDetailDto.getCityCode());
        List<String> xdata = stCommonService.buildXData(regionModels);
        List<Double> fillingQuantitys = new ArrayList<>();
        List<Double> offloadingVolumes = new ArrayList<>();

        String fillingStartTime, fillingEndTime;
        if (ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne())) {
            fillingStartTime = LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00";
            fillingEndTime = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 23:59:59";
        } else {
            if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne().getBeginDate())) {
                fillingStartTime = dpFilterParamForDetailDto.getTimeSearchOne().getBeginDate();
            } else {
                fillingStartTime = null;
            }
            if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne().getEndDate())) {
                fillingEndTime = dpFilterParamForDetailDto.getTimeSearchOne().getEndDate();
            } else {
                fillingEndTime = null;
            }
        }
        List<Map<String,Object>> list = getFillingQuantity(dpFilterParamForDetailDto, fillingStartTime, fillingEndTime);
        regionModels.forEach(r -> {
            //充装量和
            List<Map<String, Object>> regionCode = list.stream().filter(e -> e.get("regionCode").toString().contains(r.getRegionCode().toString())).collect(Collectors.toList());
            Double fillingQuantity = 0.0d;
            if (!CollectionUtils.isEmpty(regionCode)) {
                fillingQuantity = regionCode.stream().mapToDouble(e -> Double.valueOf(e.get("fillingQuantity").toString())).sum();
            }
            //卸液量和
            String orgCode = stCommonService.getAndSetOrgCode(r.getRegionCode().toString());
            List<CylinderFillingRecordStatisticsDto> cylinderFillingRecordStatisticsDtos = cylinderInfoMapper.queryoffloadingByOrgCode(orgCode, fillingStartTime, fillingEndTime);
            Double offloadingVolume = 0.0d;
            if(!ObjectUtils.isEmpty(cylinderFillingRecordStatisticsDtos)){
                List<String> collect1 = cylinderInfoMapper.queryoffloadingByOrgCode(orgCode, fillingStartTime, fillingEndTime).stream().map(e -> e.getOffloadingVolume()).collect(Collectors.toList());
                offloadingVolume =  collect1.stream().mapToDouble(Double::valueOf).sum();
            }
            fillingQuantitys.add(fillingQuantity);
            offloadingVolumes.add(offloadingVolume);
        });
        Set<Map<String,String>>  legendData = new HashSet<>();
        for (int i = 0; i < 2; i++) {
            Map<String, String> map = new HashMap<>();
            if (i == 0) {
                map.put("dataKey", "fillingQuantity");
                map.put("value", "充装量");
                map.put("chartType", "bar");
            } else {
                map.put("dataKey", "offloadingVolume");
                map.put("value", "卸液量");
                map.put("chartType", "bar");
            }
            legendData.add(map);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("xdata", xdata);
        result.put("fillingQuantity", fillingQuantitys);
        result.put("offloadingVolume", offloadingVolumes);
        result.put("legendData", legendData);
        return result;
    }

    private List<Map<String, Object>> getFillingQuantity(DPFilterParamForDetailDto dpFilterParamForDetailDto, String fillingStartTime, String fillingEndTime) {
        SearchRequest request = new SearchRequest();
        request.indices("cylinder_filling");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();

        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("regionCode", "*" + dpFilterParamForDetailDto.getCityCode() + "*"));
        //匹配气瓶类型
        if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTreeValue()) && !CylinderTypeEnum.CYLINDER.getCode().equals(dpFilterParamForDetailDto.getTreeValue())) {
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            // 是车用气瓶，进行in查询（特种气瓶包含以下三个）
            if (CylinderTypeEnum.SPECIAL_CYLINDER.getCode().equals(dpFilterParamForDetailDto.getTreeValue())) {
                Collection<String> collections = new ArrayList<>();
                collections.add("缠绕气瓶");
                collections.add("绝热气瓶");
                collections.add("内装填料气瓶");
                query.must(QueryBuilders.termsQuery("cylinderVarietyName", collections.toArray()));
            } else {
                String cylinderVarietyName = CylinderTypeEnum.of(dpFilterParamForDetailDto.getTreeValue());
                query.must(QueryBuilders.wildcardQuery("cylinderVarietyName", "*" + cylinderVarietyName + "*"));
            }
            boolMust.must(query);
        }

        SimpleDateFormat sdf = new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN);
        //充装时间
        if (!ObjectUtils.isEmpty(fillingStartTime)) {
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                query.must(QueryBuilders.rangeQuery("inspectionDateMs").gte(sdf.parse(fillingStartTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }

        if (!ObjectUtils.isEmpty(fillingEndTime) ) {
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                query.must(QueryBuilders.rangeQuery("inspectionDateAfterMS").lte(sdf.parse(fillingEndTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }

        builder.query(boolMust);
        builder.sort("inspectionDateMs",SortOrder.DESC);
        builder.sort("sequenceNbr",SortOrder.DESC);
        builder.trackTotalHits(true);
        request.source(builder);
        List<Map<String, Object>> list = new LinkedList<>();
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            for (org.elasticsearch.search.SearchHit hit : response.getHits()) {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
                Map<String,Object> map = JSONObject.toJavaObject(jsonObject.getJSONObject("sourceAsMap"), Map.class);
                list.add(map);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
       return list;
    }


    public Page<ESCylinderFillingRecordDto> getFillingSubPage(DPFilterParamForDetailDto dpFilterParamForDetailDto) {
        int current = dpFilterParamForDetailDto.getCurrent();
        int pageSize = dpFilterParamForDetailDto.getSize();
        Page<ESCylinderFillingRecordDto> result = new Page<>(current, pageSize);

        SearchRequest request = new SearchRequest();
        request.indices("cylinder_filling");

        //通用匹配规则，条件构建
        boolean flag = true;
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();

        //匹配行政区划
        if (!org.springframework.util.ObjectUtils.isEmpty(dpFilterParamForDetailDto.getCityCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.wildcardQuery("regionCode", "*" + dpFilterParamForDetailDto.getCityCode() + "*"));
            boolMust.must(query);
        }

        //匹配气瓶充装异常情况（0:正常，1：异常）
        if (!org.springframework.util.ObjectUtils.isEmpty(dpFilterParamForDetailDto.getAnomalyType())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            String status = AnomalyTypeEnum.getEnumByStatus(dpFilterParamForDetailDto.getAnomalyType()).getName();
            query.must(QueryBuilders.wildcardQuery("anomalyType", "*" + status + "*"));
            boolMust.must(query);
        }

        //匹配气瓶类型
        if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTreeValue()) && !CylinderTypeEnum.CYLINDER.getCode().equals(dpFilterParamForDetailDto.getTreeValue())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            // 是车用气瓶，进行in查询（特种气瓶包含以下三个）
            if (CylinderTypeEnum.SPECIAL_CYLINDER.getCode().equals(dpFilterParamForDetailDto.getTreeValue())) {
                Collection<String> collections = new ArrayList<>();
                collections.add("缠绕气瓶");
                collections.add("绝热气瓶");
                collections.add("内装填料气瓶");
                query.must(QueryBuilders.termsQuery("cylinderVarietyName", collections.toArray()));
            } else {
                String cylinderVarietyName = CylinderTypeEnum.of(dpFilterParamForDetailDto.getTreeValue());
                query.must(QueryBuilders.wildcardQuery("cylinderVarietyName", "*" + cylinderVarietyName + "*"));
            }
            boolMust.must(query);
        }

        String fillingStartTime = null, fillingEndTime = null;
        if (ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne())) {
            fillingStartTime = LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00";
            fillingEndTime = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 23:59:59";
        } else {
            if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne().getEndDate())) {
                fillingStartTime = dpFilterParamForDetailDto.getTimeSearchOne().getBeginDate() + " 00:00:00";
            }
            if (!ObjectUtils.isEmpty(dpFilterParamForDetailDto.getTimeSearchOne().getBeginDate())) {
                fillingEndTime = dpFilterParamForDetailDto.getTimeSearchOne().getBeginDate() + " 23:59:59";
            }
        }

        SimpleDateFormat sdf = new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN);
        //充装开始时间
        if (!ObjectUtils.isEmpty(fillingStartTime)) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                query.must(QueryBuilders.rangeQuery("inspectionDateMs").gte(sdf.parse(fillingStartTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }
        //充装结束时间
        if (!ObjectUtils.isEmpty(fillingEndTime) ) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                query.must(QueryBuilders.rangeQuery("inspectionDateAfterMS").lte(sdf.parse(fillingEndTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }
        if (flag) { // 搜索全部
            boolMust.must(QueryBuilders.matchAllQuery());
        }
        builder.query(boolMust);
        builder.sort("inspectionDateMs", SortOrder.DESC);
        builder.sort("sequenceNbr",SortOrder.DESC);
        builder.from((current - 1) * pageSize);
        builder.size(pageSize);
        builder.trackTotalHits(true);
        request.source(builder);
        List<ESCylinderFillingRecordDto> list = new LinkedList<>();
        long totle = 0;
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            for (SearchHit hit : response.getHits()) {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
                ESCylinderFillingRecordDto esCylinderFillingRecordDto1 = JSONObject.toJavaObject(jsonObject.getJSONObject("sourceAsMap"), ESCylinderFillingRecordDto.class);
                list.add(esCylinderFillingRecordDto1);
            }
            totle = response.getInternalResponse().hits().getTotalHits().value;
            result.setRecords(list);
            result.setTotal(totle);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public List<Map<String,Object>> getFillingSubAnomalyType() {
        List<Map<String,Object>> result = new ArrayList<>();
        for (AnomalyTypeEnum value : AnomalyTypeEnum.values()) {
            Map<String,Object> map = new HashMap<>();
            map.put("value",value.getCode());
            map.put("label",value.getName());
            result.add(map);
        }
        return result;
    }


    public List<SubTreeDto> getStationRateSubTree() {
        List<SubTreeDto> subTreeDtos = new ArrayList<>();
        SubTreeDto subTreeDto = new SubTreeDto();
        subTreeDto.setTitle("企业类型");
        subTreeDto.setValue("");
        List<SubTreeDto> children = new ArrayList<>();
        SubTreeDto subTree = new SubTreeDto();
        subTree.setTitle("充装企业");
        subTree.setValue("1231");
        children.add(subTree);
        subTreeDto.setChildren(children);
        subTreeDtos.add(subTreeDto);
        return subTreeDtos;
    }

    public Map<String, Object> getStationRateSubChart(DPFilterParamForDetailDto dpFilterParamForDetailDto) {
        List<RegionModel> regionModels = stCommonService.setRegionIfRootParentAndNoAccessIf3Level(dpFilterParamForDetailDto.getCityCode());
        List<String> xdata = stCommonService.buildXData(regionModels);
        List<String> counts = new ArrayList<>();
        List<String> stationRates = new ArrayList<>();
        List<RegionModel> regionList = stCommonService.setRegionIfRootParent(dpFilterParamForDetailDto.getCityCode());
        List<Map<String,Object>> returnList = regionList.parallelStream().map(r -> {
            String orgCode = stCommonService.getAndSetOrgCode(r.getRegionCode().toString());
            Map<String, Object> item = new HashMap<>();
            item.put("regionCode", r.getRegionCode());
            getStationRate(orgCode, item);
            return item;
        }).collect(Collectors.toList());
        regionModels.forEach(r -> {
            String count = returnList.stream().filter(e -> e.get("regionCode").toString().equals(r.getRegionCode().toString())).collect(Collectors.toList()).get(0).get("totalNum").toString();
            String stationRate = returnList.stream().filter(e -> e.get("regionCode").toString().equals(r.getRegionCode().toString())).collect(Collectors.toList()).get(0).get("stationRate").toString();
            counts.add(count);
            stationRates.add(stationRate);
        });
        Map<String, Object> result = new HashMap<>();
        Set<Map<String,String>>  legendData = new HashSet<>();
        for (int i = 0; i < 2; i++) {
            Map<String, String> map = new HashMap<>();
            if (i == 0) {
                map.put("dataKey", "count");
                map.put("value", "对接企业数量");
                map.put("chartType", "bar");
            } else {
                map.put("dataKey", "stationRate");
                map.put("value", "对接率");
                map.put("chartType", "line");
            }
            legendData.add(map);
        }
        result.put("legendData", legendData);
        result.put("xdata", xdata);
        result.put("count", counts);
        result.put("stationRate", stationRates);
        return result;
    }


    public IPage<TzBaseEnterpriseInfoDto> getStationRateSubPage(DPFilterParamForDetailDto dpFilterParamForDetailDto) {
        Page<TzBaseEnterpriseInfoDto> page = new Page<>(dpFilterParamForDetailDto.getCurrent(), dpFilterParamForDetailDto.getSize());
        String orgCode = stCommonService.getAndSetOrgCode(dpFilterParamForDetailDto.getCityCode());
        if(ObjectUtils.isEmpty(orgCode)){
            return null;
        }
        return cylinderStatisticsMapper.getStationRateSubPage(page, dpFilterParamForDetailDto.getCompanyName(), orgCode);
    }

    public IPage<CylinderFillingMessageModel> getUploadCylinderLogs(PageParam pageParam, String regionCode) {
        CompanyModel result = Privilege.companyClient.queryByCompanyCode(regionCode).getResult();
        Page<CylinderFillingMessageModel> page = new Page<>(pageParam.getCurrent(), pageParam.getSize());
        if(result != null){
            IPage<CylinderFillingMessageModel> uploadCylinderLogsByOrgCode = cylinderOpenApiService.getUploadCylinderLogsByOrgCode(page, result.getOrgCode());
            List<String> appIds = uploadCylinderLogsByOrgCode.getRecords().stream().map(CylinderFillingMessageModel::getAppId).collect(Collectors.toList()).stream().filter(x -> !ValidationUtil.isEmpty(x)).distinct().collect(Collectors.toList());
            if(!appIds.isEmpty()){
                List<TzBaseEnterpriseInfo> tzBaseEnterpriseInfoList = tzBaseEnterpriseInfoMapper.selectList(new LambdaQueryWrapper<TzBaseEnterpriseInfo>().in(TzBaseEnterpriseInfo::getAppId, appIds));
                if (!ValidationUtil.isEmpty(tzBaseEnterpriseInfoList)){
                    Map<String, String> map = tzBaseEnterpriseInfoList.stream().collect(Collectors.toMap(TzBaseEnterpriseInfo::getAppId, TzBaseEnterpriseInfo::getUseUnitCode));
                    uploadCylinderLogsByOrgCode.getRecords().forEach(x -> {
                        x.setUseUnitCode(map.get(x.getAppId()));
                    });
                }
            }
        }
        return page;
    }

}