package com.yeejoin.amos.boot.module.elevator.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.enums.ReginStepEnum;
import com.yeejoin.amos.boot.module.elevator.api.dto.AlertMaintenanceUnitStatisticsDto;
import com.yeejoin.amos.boot.module.elevator.api.dto.AlertPaperInfoDto;
import com.yeejoin.amos.boot.module.elevator.api.dto.AlertUseUnitStatisticsDto;
import com.yeejoin.amos.boot.module.elevator.api.entity.AlertMaintenanceUnitStatistics;
import com.yeejoin.amos.boot.module.elevator.api.entity.AlertRescueStatistics;
import com.yeejoin.amos.boot.module.elevator.api.entity.AlertUseUnitStatistics;
import com.yeejoin.amos.boot.module.elevator.api.entity.Elevator;
import com.yeejoin.amos.boot.module.elevator.api.mapper.*;
import com.yeejoin.amos.feign.privilege.Privilege;
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.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.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

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

    private YjBaseMapper yjBaseMapper;

    private AlertCalledMapper alertCalledMapper;

    private ElevatorMapper elevatorMapper;

    private AlertRescueStatisticsMapper alertRescueStatisticsMapper;

    private AlertUseUnitStatisticsMapper alertUseUnitStatisticsMapper;

    private AlertMaintenanceUnitStatisticsMapper alertMaintenanceUnitStatisticsMapper;

    private AlertStatisticsMapper alertStatisticsMapper;

    @Autowired
    AlertCalledServiceImpl alertCalledService;

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

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

    public DPStatisticsServiceImpl(YjBaseMapper yjBaseMapper, AlertCalledMapper alertCalledMapper, ElevatorMapper elevatorMapper, AlertRescueStatisticsMapper alertRescueStatisticsMapper, AlertUseUnitStatisticsMapper alertUseUnitStatisticsMapper, AlertMaintenanceUnitStatisticsMapper alertMaintenanceUnitStatisticsMapper, AlertStatisticsMapper alertStatisticsMapper) {
        this.yjBaseMapper = yjBaseMapper;
        this.alertCalledMapper = alertCalledMapper;
        this.elevatorMapper = elevatorMapper;
        this.alertRescueStatisticsMapper = alertRescueStatisticsMapper;
        this.alertUseUnitStatisticsMapper = alertUseUnitStatisticsMapper;
        this.alertMaintenanceUnitStatisticsMapper = alertMaintenanceUnitStatisticsMapper;
        this.alertStatisticsMapper = alertStatisticsMapper;
    }

    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<Integer> ydata = new ArrayList<>();
        Map<String, Integer> map =  new HashMap<>();

        regionList.parallelStream().forEach(x -> {
            LambdaQueryWrapper<Elevator> lambda = new QueryWrapper<Elevator>().lambda();
            lambda.like(Elevator::getRegionCode, x.getRegionCode());
            map.put(x.getRegionName(), elevatorMapper.selectCount(lambda));
        });
        xdata.stream().forEach(x -> {
            ydata.add(map.get(x));
        });
        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 List<AlertPaperInfoDto> instantAlert(DPFilterParamDto dpFilterParamDto) throws Exception {
        List<String> regionCodes = new ArrayList<>();
        regionCodes.add(dpFilterParamDto.getCityCode());
        List<AlertPaperInfoDto> alertPaperInfoDtoList = alertCalledService.getAlertPaperInfoList(alertCalledService.authRegionCodeList(regionCodes), false);
        return alertPaperInfoDtoList;
    }

    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<AlertMaintenanceUnitStatisticsDto> rankUnitByRescueTime(DPFilterParamDto dpFilterParamDto) throws Exception {
        String date = DateUtil.formatDate(new Date(), "yyyy-MM");
        String orgCode = this.getAndSetOrgCode(dpFilterParamDto);

        LambdaQueryWrapper<AlertMaintenanceUnitStatistics> lambda = new QueryWrapper<AlertMaintenanceUnitStatistics>().lambda();
        lambda.likeLeft(AlertMaintenanceUnitStatistics::getSupervisoryUnitOrgCode, orgCode);
        lambda.eq(AlertMaintenanceUnitStatistics :: getStatisticsDate, date);
        List<AlertMaintenanceUnitStatistics> alertMaintenanceUnitStatistics = alertMaintenanceUnitStatisticsMapper.selectList(lambda);
        List<AlertMaintenanceUnitStatisticsDto> models = Bean.toModels(alertMaintenanceUnitStatistics, AlertMaintenanceUnitStatisticsDto.class);
        return models;
    }

    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 = yjBaseMapper.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 = yjBaseMapper.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());
        }
    }
}
