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

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.dto.CountDto;
import com.yeejoin.amos.boot.module.common.api.constant.TZSCommonConstant;
import com.yeejoin.amos.boot.module.common.api.dto.DPFilterParamDto;
import com.yeejoin.amos.boot.module.common.api.enums.ReginStepEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.DPMapStatisticsItemEnum;
import com.yeejoin.amos.boot.module.statistics.api.mapper.CommonBaseMapper;
import com.yeejoin.amos.boot.module.statistics.api.mapper.ZLStatisticsMapper;
import com.yeejoin.amos.boot.module.ymt.api.dto.EquipmentCategoryDto;
import com.yeejoin.amos.boot.module.ymt.api.enums.EquimentEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.EquipTechParamPipelineMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.EquipmentCategoryMapper;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
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.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.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Service
public class StCommonServiceImpl {

    /**
     * 压力容器设备种类
     */
    public final static String EQU_LIST_CYLINDER = "2000";

    /**
     * 气瓶设备类别
     */
    public final static String EQU_CATEGORY_CYLINDER = "2300";

    private CommonBaseMapper commonMapper;

    private RestHighLevelClient restHighLevelClient;

    private EquipTechParamPipelineMapper techParamsPipelineMapper;

    private EquipmentCategoryMapper equipmentCategoryMapper;

    private ZLStatisticsMapper zlStatisticsMapper;

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

    private static List<RegionModel> regionModels = new ArrayList<>();

    private static List<EquipmentCategoryDto> equipmentCategoryDtos;

    public StCommonServiceImpl(CommonBaseMapper commonMapper, RestHighLevelClient restHighLevelClient, EquipTechParamPipelineMapper techParamsPipelineMapper, EquipmentCategoryMapper equipmentCategoryMapper, ZLStatisticsMapper zlStatisticsMapper) {
        this.commonMapper = commonMapper;
        this.restHighLevelClient = restHighLevelClient;
        this.techParamsPipelineMapper = techParamsPipelineMapper;
        this.equipmentCategoryMapper = equipmentCategoryMapper;
        this.zlStatisticsMapper = zlStatisticsMapper;
    }

    public static Map<String, String> getRegionCodeOrgCodeMap() {
        return regionCodeOrgCodeMap;
    }

    public static List<EquipmentCategoryDto> getEquipmentCategory() {
        return equipmentCategoryDtos;
    }

    public void init() {
        // 数据不变所以放到内存，提高响应时间
        equipmentCategoryDtos = equipmentCategoryMapper.selectClassify();
        initReginCode();
    }

    private void initReginCode() {
        Collection<RegionModel> result = Systemctl.regionClient.queryForTree(null).getResult();
        result.forEach(r -> {
            regionModels.add(r);
            this.loopSetChildRegin(regionModels, r.getChildren());
        });
    }

    private void loopSetChildRegin(List<RegionModel> regionModels, Collection<RegionModel> children) {
        if (children != null && children.size() > 0) {
            children.forEach(c -> {
                regionModels.add(c);
                this.loopSetChildRegin(regionModels, c.getChildren());
            });
        }
    }

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

    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;
    }

    public List<RegionModel> getUserRegionCode(ReginParams selectedOrgInfo) {
        String regionCode = selectedOrgInfo.getCompany().getCompanyCode();
        Optional<RegionModel> op = regionModels.stream().filter(e -> e.getRegionCode().toString().equals(regionCode)).findFirst();
        RegionModel model = op.orElse(new RegionModel());
        RegionModel result = new RegionModel();
        result.setRegionName(model.getRegionName());
        result.setRegionCode(Integer.parseInt(regionCode));
        return Collections.singletonList(result);
    }

    public List<RegionModel> setRegionIfRootParent(DPFilterParamDto dpFilterParamDto) {
        List<RegionModel> regions = regionModels.parallelStream().filter(e -> e.getParentRegionCode() != null && (e.getParentRegionCode().toString()).equals(dpFilterParamDto.getCityCode())).collect(Collectors.toList());
        // 陕西省时需要在地图返回独立的地级市:韩城、杨凌、西咸
        if (dpFilterParamDto.getCityCode().equals(TZSCommonConstant.SHAN_XI_REGION_CODE)) {
            List<RegionModel> independentRegions = ReginStepEnum.enum2RegionList("map");
            regions.addAll(independentRegions);
        }
        return regions;
    }

    public List<RegionModel> setRegionIfRootParent(String regionCode) {
        List<RegionModel> regions = regionModels.parallelStream().filter(e -> e.getParentRegionCode() != null && (e.getParentRegionCode().toString()).equals(regionCode)).collect(Collectors.toList());
        // 陕西省时需要在地图返回独立的地级市:韩城、杨凌、西咸
        if (regionCode.equals(TZSCommonConstant.SHAN_XI_REGION_CODE)) {
            List<RegionModel> independentRegions = ReginStepEnum.enum2RegionList("map");
            regions.addAll(independentRegions);
        }
        return regions;
    }

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

    public long staticsCenterMapCountDataForCylinder(Map<String, Object> result, String orgCode) {
        long num = 0;
        CountRequest request = new CountRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 设备类别精确查询气瓶
        boolMust.must(QueryBuilders.termsQuery("EQU_CATEGORY_CODE", EQU_CATEGORY_CYLINDER));
        // 且在用状态设备
        boolMust.must(QueryBuilders.termQuery("EQU_STATE", EquimentEnum.ZAIYONG.getCode()));

        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        result.put(DPMapStatisticsItemEnum.GAS.getCode(), num);
        return num;
    }

    public void staticsCenterMapCountDataForEquip(Map<String, Object> result, long cylinderNum, String orgCode) {
        SearchRequest request = new SearchRequest();
        request.indices("idx_biz_view_jg_all");
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();
        // 按照管辖机构区域信息模糊查询
        boolMust.must(QueryBuilders.wildcardQuery("ORG_BRANCH_CODE.keyword", QueryParser.escape(orgCode) + "*"));
        // 且在用状态设备
        boolMust.must(QueryBuilders.termQuery("EQU_STATE", EquimentEnum.ZAIYONG.getCode()));
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(boolMust);
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("count_by_equ_list_code").field("EQU_LIST_CODE");
        builder.aggregation(aggregationBuilder);
        request.source(builder);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            Terms terms = response.getAggregations().get("count_by_equ_list_code");
            Map<String, Long> countMap = new HashMap<>();
            for (Terms.Bucket bucket : terms.getBuckets()) {
                // 压力容器里包括气瓶所以需要特殊处理，在统计压力容器时去掉气瓶的数量
                if (bucket.getKeyAsString().equals(EQU_LIST_CYLINDER)) {
                    countMap.put(bucket.getKeyAsString(), bucket.getDocCount() - cylinderNum);
                } else {
                    countMap.put(bucket.getKeyAsString(), bucket.getDocCount());
                }
            }
            // 按照8大类枚举，进行加工。目的：固定八大类防止没统计数据导致缺少分类、将设备种类的code换成前端定义的key
            equipmentCategoryDtos.forEach(c -> {
                result.put(this.castCategoryCode2WebCode(c.getCode()), countMap.getOrDefault(c.getCode(), 0L));
            });
            result.put(DPMapStatisticsItemEnum.TOTAL.getCode(), countMap.values().stream().mapToLong(e -> e).sum() + cylinderNum);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        result.remove(DPMapStatisticsItemEnum.PRESSURE_PIPELINES.getCategory());
    }

    private String castCategoryCode2WebCode(String category) {
        DPMapStatisticsItemEnum itemEnum = DPMapStatisticsItemEnum.getInstanceByCategory(category);
        return itemEnum.getCode();
    }

    public void staticsCenterMapCountDataForPipeline(Map<String, Object> result, String orgCode) {
        String length = techParamsPipelineMapper.sumPipeLengthByOrgCode(orgCode);
        BigDecimal lengthDecimal = new BigDecimal(length);
        if (lengthDecimal.compareTo(BigDecimal.ZERO) > 0) {
            // 数据库的米换算成千米
            length = lengthDecimal.divide(new BigDecimal("1000"), 3, RoundingMode.HALF_UP).toPlainString();
        }
        result.put(DPMapStatisticsItemEnum.PRESSURE_PIPELINES.getCode(), length);
    }

    public CountDto getRedStatusCompany(DPFilterParamDto dpFilterParamDto) {
        CountDto countDto = new CountDto();
        countDto.setLongValue(this.countCompanyForCertDateTimeOut(dpFilterParamDto));
        countDto.setLabel("许可超期");
        return countDto;
    }

    public CountDto getYellowStatusCompany(DPFilterParamDto dpFilterParamDto) {
        CountDto countDto = new CountDto();
        countDto.setLongValue(this.countCompanyForCertDateTemporary(dpFilterParamDto));
        countDto.setLabel("许可临期");
        return countDto;
    }



    private long countCompanyForCertDateTemporary(DPFilterParamDto dpFilterParamDto) {
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto.getCityCode());
        // 临期6个月
        String limitDate = DateUtil.offset(DateUtil.date(), DateField.DAY_OF_MONTH, 6).toDateStr();
        return zlStatisticsMapper.countCompanyForCertDateTemporary(dpFilterParamDto, orgCode, limitDate);
    }

    private long countCompanyForCertDateTimeOut(DPFilterParamDto dpFilterParamDto) {
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto.getCityCode());
        String limitDate = DateUtil.today();
        return zlStatisticsMapper.countCompanyForCertDateTimeOut(dpFilterParamDto, orgCode, limitDate);
    }

}
