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

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.entity.AlertRescueStatistics;
import com.yeejoin.amos.boot.module.common.api.entity.AlertUseUnitStatistics;
import com.yeejoin.amos.boot.module.common.api.enums.ReginStepEnum;
import com.yeejoin.amos.boot.module.statistics.api.dto.AlertUseUnitStatisticsDto;
import com.yeejoin.amos.boot.module.statistics.api.mapper.AlertRescueStatisticsMapper;
import com.yeejoin.amos.boot.module.statistics.api.mapper.AlertStatisticsMapper;
import com.yeejoin.amos.boot.module.statistics.api.mapper.AlertUseUnitStatisticsMapper;
import com.yeejoin.amos.boot.module.statistics.api.mapper.CommonBaseMapper;
import com.yeejoin.amos.boot.module.ymt.api.dto.AlertPaperInfoDto;
import com.yeejoin.amos.boot.module.ymt.api.entity.DispatchPaper;
import com.yeejoin.amos.boot.module.ymt.api.enums.EquipmentClassifityEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.AlertCalledMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.DispatchPaperMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.DispatchTaskMapper;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.AgencyUserModel;
import com.yeejoin.amos.feign.privilege.model.CompanyModel;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.queryparser.classic.QueryParser;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.DateTimeUtil;
import org.typroject.tyboot.core.foundation.utils.DateUtil;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 大屏统计实现类
 *
 * @author Administrator
 */
@Service
@Slf4j
public class YJDPStatisticsServiceImpl {

    private CommonBaseMapper commonBaseMapper;

    private AlertCalledMapper alertCalledMapper;

    private AlertRescueStatisticsMapper alertRescueStatisticsMapper;

    private AlertUseUnitStatisticsMapper alertUseUnitStatisticsMapper;

    private AlertStatisticsMapper alertStatisticsMapper;

    private DispatchTaskMapper dispatchTaskMapper;


    private DispatchPaperMapper dispatchPaperMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

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

    private static Map<String, List<RegionModel>> regionChildRegionMap = new ConcurrentHashMap<>();

    public YJDPStatisticsServiceImpl(CommonBaseMapper commonBaseMapper, AlertCalledMapper alertCalledMapper, AlertRescueStatisticsMapper alertRescueStatisticsMapper, AlertUseUnitStatisticsMapper alertUseUnitStatisticsMapper, AlertStatisticsMapper alertStatisticsMapper, DispatchTaskMapper dispatchTaskMapper, DispatchPaperMapper dispatchPaperMapper, RestHighLevelClient restHighLevelClient) {
        this.commonBaseMapper = commonBaseMapper;
        this.alertCalledMapper = alertCalledMapper;
        this.alertRescueStatisticsMapper = alertRescueStatisticsMapper;
        this.alertUseUnitStatisticsMapper = alertUseUnitStatisticsMapper;
        this.alertStatisticsMapper = alertStatisticsMapper;
        this.dispatchTaskMapper = dispatchTaskMapper;
        this.dispatchPaperMapper = dispatchPaperMapper;
        this.restHighLevelClient = restHighLevelClient;
    }

    public JSONObject eventStatByDay(DPFilterParamDto dpFilterParamDto) throws Exception {
        String beginDate = DateUtil.formatDate(DateTimeUtil.addDays(new Date(), -6), DateUtil.Y_M_D);
        String endDate = DateUtil.getNow(DateUtil.Y_M_D);
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);

        List<String> everyDay = this.getEveryDay(beginDate, endDate);
        JSONObject jsonObject = this.buildLegendData(null, "trapped", "困人");
        this.buildLegendData(jsonObject, "complaint", "投诉");
        this.buildLegendData(jsonObject, "fault", "故障");

        List<String> xdata = new ArrayList<>();
        List<Object> trapped = new ArrayList<>();
        List<Object> complaint = new ArrayList<>();
        List<Object> fault = new ArrayList<>();
        List<Map<String, Object>> maps = alertCalledMapper.queryNearlyList(beginDate, endDate, orgCode);
        everyDay.stream().forEach(day -> {
            try {
                xdata.add(DateUtil.formatDate(DateUtil.formatStringToDate(day, DateUtil.Y_M_D), "MM.dd"));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            Map<String, Object> map = maps.stream().filter(x -> day.equals(x.get("day"))).collect(Collectors.toMap(
                    item -> (String) item.get("alarmType"),
                    item -> item.get("majorAlertCount")));
            trapped.add(!ValidationUtil.isEmpty(map.get("困人救援")) ? Integer.valueOf(map.get("困人救援").toString()) : 0);
            complaint.add(!ValidationUtil.isEmpty(map.get("投诉咨询")) ? Integer.valueOf(map.get("投诉咨询").toString()) : 0);
            fault.add(!ValidationUtil.isEmpty(map.get("故障维修")) ? Integer.valueOf(map.get("故障维修").toString()) : 0);
        });

        jsonObject.put("xdata", xdata);
        jsonObject.put("trapped", trapped);
        jsonObject.put("complaint", complaint);
        jsonObject.put("fault", fault);
        return jsonObject;
    }

    public JSONObject elevatorCountStat(DPFilterParamDto dpFilterParamDto) throws Exception {
        List<RegionModel> regionList = this.getRegionList(dpFilterParamDto);
        List<String> xdata = regionList.stream().map(r -> r.getRegionName().toString()).collect(Collectors.toList());

        List<Long> ydata = new ArrayList<>();
        regionList.stream().forEach(x -> {
            String orgCode = this.getAndSetOrgCode(x.getRegionCode().toString());
            ydata.add(elevatorCountByES(orgCode));
        });
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("xdata", xdata);
        jsonObject.put("ydata", ydata);
        return jsonObject;
    }

    public JSONObject eventStatByLocation(DPFilterParamDto dpFilterParamDto) throws Exception {
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);
        dpFilterParamDto.setBeginDate(DateUtil.formatDate(DateTimeUtil.getMinValueOfOneDay(DateTimeUtil.addMonths(new Date(), -1)), DateUtil.Y_M_D_HMS));
        dpFilterParamDto.setEndDate(DateUtil.formatDate(DateTimeUtil.getMaxValueOfOneDay(new Date()), DateUtil.Y_M_D_HMS));

        List<Map<String, Object>> maps = alertCalledMapper.queryListByLocation(orgCode, dpFilterParamDto);
        List<Object> xdata = maps.stream().map(item -> item.get("name")).collect(Collectors.toList());
        List<Object> ydata = maps.stream().map(item -> item.get("majorAlertCount")).collect(Collectors.toList());

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("xdata", xdata);
        jsonObject.put("ydata", ydata);
        return jsonObject;
    }

    public JSONObject faultCauseRatio(DPFilterParamDto dpFilterParamDto) throws Exception {
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);
        List<Map<String, Object>> maps = alertCalledMapper.faultCauseRatio(orgCode);

        List<Object> xdata = maps.stream().map(item -> item.get("errorResult")).collect(Collectors.toList());
        List<Object> ydata = maps.stream().map(item -> item.get("majorAlertCount")).collect(Collectors.toList());

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("xdata", xdata);
        jsonObject.put("ydata", ydata);
        return jsonObject;
    }

    public List<AlertPaperInfoDto> instantAlert(DPFilterParamDto dpFilterParamDto) {
        List<String> regionCodes = new ArrayList<>();
        regionCodes.add(dpFilterParamDto.getCityCode());
        List<AlertPaperInfoDto> alertPaperInfoDtoList = this.getAlertPaperInfoList(this.authRegionCodeList(regionCodes), false);
        return alertPaperInfoDtoList;
    }

    public List<AlertPaperInfoDto> getAlertPaperInfoList(List<String> regionCodes, Boolean isHistory) {
        List<AlertPaperInfoDto> temp = alertCalledMapper.getAlertPaperInfoList(regionCodes, isHistory);
        temp.forEach(t -> {
            DispatchPaper paper = dispatchPaperMapper.selectOne(new LambdaQueryWrapper<DispatchPaper>().eq(DispatchPaper::getIsDelete, false).eq(DispatchPaper::getAlertId, t.getAlertId()));
            if (paper != null) {
                t.setFinishTime(paper.getFeedbackFinishTime());
            }
        });
        return temp;
    }

    public List<String> getUserRegionCode() {
        List<String> regionList = new ArrayList<>();
        AgencyUserModel me = Privilege.agencyUserClient.getme().getResult();
        CompanyModel userCompany = me.getCompanys().get(0);
        String regions = userCompany.getRegionSeq();
        if (regions != null) {
            String[] regionsId = regions.split(",");
            for (String regionId : regionsId) {
                RegionModel region = Systemctl.regionClient.getRegion(Long.valueOf(regionId)).getResult();
                regionList.add(region.getRegionCode() + "");
            }
        }
        return regionList;
    }

    public List<String> authRegionCodeList(List<String> regionCodes) {
        // 警情id 警情地址 警情状态 警情类型 警情发生时间
        List<String> useRegionCode = this.getUserRegionCode();
        if (regionCodes == null) {
            regionCodes = useRegionCode;
        } else {
            // 判断传入regionCodes 是否越权
            List<String> tempList = new ArrayList<>();
            Iterator<String> regionIt = regionCodes.iterator();
            while (regionIt.hasNext()) {
                String tempCode = regionIt.next();
                String districtCode = tempCode.substring(4, 6);
                String provinceCode = tempCode.substring(2, 6);
                if ("0000".equals(provinceCode)) {// 为省则默认使用用户现有权限查询数据
                    regionCodes = useRegionCode;
                } else {
                    if ("00".equals(districtCode)) { // 为市
                        if (!useRegionCode.contains(tempCode)) { // 不包含该市权限 需要移除市搜索条件 添加 该市下区搜索条件
                            for (String tempUsercode : useRegionCode) {
                                if (tempUsercode.contains(tempCode.substring(0, 4))) {
                                    tempList.add(tempUsercode);
                                }
                            }
                            regionIt.remove();
                        }
                    } else { // 为区 不会出现越权情况

                    }
                }
            }
            regionCodes.addAll(tempList);
        }
        return regionCodes;
    }

    public JSONObject trappedUserCount(DPFilterParamDto dpFilterParamDto) throws Exception {
        String date = DateUtil.formatDate(new Date(), "yyyy-MM");
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);

        LambdaQueryWrapper<AlertRescueStatistics> lambda = new QueryWrapper<AlertRescueStatistics>().lambda();
        lambda.likeLeft(AlertRescueStatistics::getSupervisoryUnitOrgCode, orgCode);
        lambda.eq(AlertRescueStatistics::getStatisticsDate, date);
        List<AlertRescueStatistics> alertRescueStatistics = alertRescueStatisticsMapper.selectList(lambda);

        List<Long> ydata = new ArrayList<>();
        ydata.add(alertRescueStatistics.stream().collect(Collectors.summingLong(AlertRescueStatistics::getLessNum)));
        ydata.add(alertRescueStatistics.stream().collect(Collectors.summingLong(AlertRescueStatistics::getGreaterNum)));
        ydata.add(alertRescueStatistics.stream().collect(Collectors.summingLong(AlertRescueStatistics::getLevelOne)));
        ydata.add(alertRescueStatistics.stream().collect(Collectors.summingLong(AlertRescueStatistics::getLevelTwo)));
        ydata.add(alertRescueStatistics.stream().collect(Collectors.summingLong(AlertRescueStatistics::getLevelThree)));

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("xdata", Arrays.asList("30分钟内到达次数", "超过30分钟到次数", "一级救援次数", "二级救援次数", "三级救援次数"));
        jsonObject.put("ydata", ydata);
        return jsonObject;
    }

    public List<AlertUseUnitStatisticsDto> trappedUserHighCompanyCount(DPFilterParamDto dpFilterParamDto) throws Exception {
        String date = DateUtil.formatDate(new Date(), "yyyy-MM");
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);

        LambdaQueryWrapper<AlertUseUnitStatistics> lambda = new QueryWrapper<AlertUseUnitStatistics>().lambda();
        lambda.likeLeft(AlertUseUnitStatistics::getSupervisoryUnitOrgCode, orgCode);
        lambda.eq(AlertUseUnitStatistics::getStatisticsDate, date);
        List<AlertUseUnitStatistics> alertUseUnitStatistics = alertUseUnitStatisticsMapper.selectList(lambda);
        return Bean.toModels(alertUseUnitStatistics, AlertUseUnitStatisticsDto.class);
    }

    public List<Map<String, Object>> rankUnitByRescueTime(DPFilterParamDto dpFilterParamDto) {
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);
        List<Map<String, Object>> maps = dispatchTaskMapper.rankUnitByRescueTime(orgCode);
        maps.stream().forEach(x -> {
            String avgTime = "";
            Long seconds = Long.valueOf(x.get("avgTime").toString());
            long days = TimeUnit.SECONDS.toDays(seconds);
            long hours = TimeUnit.SECONDS.toHours(seconds) - TimeUnit.DAYS.toHours(days);
            long minutes = TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.HOURS.toMinutes(hours);
            if (!ValidationUtil.isEmpty(days) && days > 0) {
                avgTime = avgTime + days + "天";
            }
            if (!ValidationUtil.isEmpty(hours) && hours > 0) {
                avgTime = avgTime + hours + "小时";
            }
            if (!ValidationUtil.isEmpty(minutes) && minutes > 0) {
                avgTime = avgTime + minutes + "分钟";
            }
            if (seconds < 60) {
                avgTime = avgTime + seconds + "秒";
            }
            x.put("avgTime", avgTime);
        });
        return maps;
    }

    public JSONObject regionEventRank(DPFilterParamDto dpFilterParamDto) throws Exception {
        List<RegionModel> childRegion = this.getRegionList(dpFilterParamDto);
        JSONObject jsonObject = new JSONObject();

        Map<String, Long> map = new HashMap<>();
        childRegion.stream().forEach(x -> {
            String orgCode = this.getAndSetOrgCode(x.getRegionCode().toString());
            if (ValidationUtil.isEmpty(orgCode)) {
                map.put(x.getRegionName(), 0L);
            } else {
                Long num = alertStatisticsMapper.countEmergencyEventsByOrgCodeAndDate(orgCode, dpFilterParamDto);
                map.put(x.getRegionName(), num);
            }
        });

        List<String> xdata = new ArrayList<>(map.keySet());
        List<Long> ydata = new ArrayList<>();
        xdata.sort(Comparator.comparingLong(map::get).reversed());

        xdata.stream().forEach(x -> {
            ydata.add(map.get(x));
        });
        jsonObject.put("xdata", xdata);
        jsonObject.put("ydata", ydata);
        return jsonObject;
    }

    private List<RegionModel> getRegionList(DPFilterParamDto dpFilterParamDto) {
        try {
            if (regionChildRegionMap.get(dpFilterParamDto.getCityCode()) == null) {
                List<RegionModel> regionModels = this.setRegionIfRootParent(dpFilterParamDto.getCityCode());
                regionChildRegionMap.put(dpFilterParamDto.getCityCode(), regionModels);
                return regionModels;
            } else {
                return regionChildRegionMap.get(dpFilterParamDto.getCityCode());
            }
        } catch (Exception e) {
            log.error("查询行政区域失败：{}", e.getMessage());
        }
        return new ArrayList<>();
    }

    private List<RegionModel> setRegionIfRootParent(String regionCode) throws Exception {
        List<RegionModel> regionList = Systemctl.regionClient.queryByParentRegionCode(Integer.parseInt(regionCode)).getResult();
        // 陕西省时需要在地图返回独立的地级市:韩城、杨凌、西咸
        if (regionCode.equals(TZSCommonConstant.SHAN_XI_REGION_CODE)) {
            List<RegionModel> independentRegions = ReginStepEnum.enum2RegionList("map");
            regionList.addAll(independentRegions);
        }
        regionList = regionList.stream().sorted(Comparator.comparing(RegionModel::getRegionCode, Comparator.naturalOrder())).collect(Collectors.toList());
        return regionList;
    }

    private String getAndSetOrgCode(DPFilterParamDto dpFilterParamDto) {
        this.getRegionList(dpFilterParamDto);
        String orgCode = regionCodeOrgCodeMap.get(dpFilterParamDto.getCityCode());
        if (orgCode == null) {
            orgCode = commonBaseMapper.getOrgCodeByCompanyCode(dpFilterParamDto.getCityCode());
            if (orgCode == null) {
                return "";
            }
            regionCodeOrgCodeMap.put(dpFilterParamDto.getCityCode(), orgCode);
        }
        return orgCode;
    }

    private String getAndSetOrgCode(String cityCode) {
        String orgCode = regionCodeOrgCodeMap.get(cityCode);
        if (orgCode == null) {
            orgCode = commonBaseMapper.getOrgCodeByCompanyCode(cityCode);
            if (orgCode == null) {
                return "";
            }
            regionCodeOrgCodeMap.put(cityCode, orgCode);
        }
        return orgCode;
    }

    /**
     * 获取两个时间段之间的每一天
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 结果
     */
    public List<String> getEveryDay(String startTime, String endTime) {
        List<String> dateList = new ArrayList<>();
        LocalDate startDate = LocalDate.parse(startTime);
        LocalDate endDate = LocalDate.parse(endTime);
        long numOfDays = ChronoUnit.DAYS.between(startDate, endDate);
        for (int i = 0; i <= numOfDays; i++) {
            LocalDate currentDate = startDate.plusDays(i);
            String format = currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            dateList.add(format);
        }
        return dateList;
    }

    private JSONObject buildLegendData(JSONObject jsonObject, String dataKey, String value) {
        if (ValidationUtil.isEmpty(jsonObject)) {
            jsonObject = new JSONObject();
        }
        JSONObject legendData = jsonObject.getJSONObject("legendData");
        if (ValidationUtil.isEmpty(legendData)) {
            legendData = new JSONObject();
            jsonObject.put("legendData", legendData);
        }
        legendData.put(dataKey, value);
        return jsonObject;
    }

    public List<Map<String, Object>> getCenterMapCountDataForOverview(DPFilterParamDto dpFilterParamDto) {
        return getRegionList(dpFilterParamDto).parallelStream().map(r -> {
            DPFilterParamDto filterParamDto = new DPFilterParamDto();
            filterParamDto.setCityCode(r.getRegionCode().toString());
            String orgCode = this.getAndSetOrgCode(filterParamDto);
            this.setDefaultFilter(filterParamDto);
            Map<String, Object> itemResult = getCenterMapOverviewData(orgCode, filterParamDto);
            itemResult.put("regionCode", r.getRegionCode());
            itemResult.put("regionName", r.getRegionName());
            return itemResult;
        }).collect(Collectors.toList());
    }

    private Map<String, Object> getCenterMapOverviewData(String orgCode, DPFilterParamDto filterParamDto) {
        Map<String, Object> result = new HashMap<>();
        // 统计完成的事件包括困人、故障
        Long num = alertStatisticsMapper.countEmergencyEventsByOrgCodeAndDate(orgCode, filterParamDto);
        result.put("alarmCount", num);
        return result;
    }


    public Map<String, Object> getCenterMapCountDataForGlobal(DPFilterParamDto dpFilterParamDto) {
        this.setDefaultFilter(dpFilterParamDto);
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);
        // 1.报检数量统计
        return this.getCenterMapOverviewData(orgCode, dpFilterParamDto);
    }

    private void setDefaultFilter(DPFilterParamDto dpFilterParamDto) {
        if (StringUtils.isEmpty(dpFilterParamDto.getBeginDate())) {
            dpFilterParamDto.setBeginDate(cn.hutool.core.date.DateUtil.lastMonth().toDateStr());
        }
        if (StringUtils.isEmpty(dpFilterParamDto.getEndDate())) {
            dpFilterParamDto.setEndDate(cn.hutool.core.date.DateUtil.today());
        }
    }

    private long elevatorCountByES(String orgCode) {
        long num = 0L;
        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.termQuery("EQU_LIST_CODE", EquipmentClassifityEnum.DT.getCode()));
        request.query(boolMust);
        try {
            CountResponse response = restHighLevelClient.count(request, RequestOptions.DEFAULT);
            num = response.getCount();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return num;
    }
}
