package com.zcxk.service;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zcxk.common.StringUtils;
import com.zcxk.dao.DeviceSceneMapper;
import com.zcxk.dto.*;
import com.zcxk.dto.scene.SceneIdDto;
import com.zcxk.entity.MonitorDataEntity;
import com.zcxk.entity.MonitorDataValueEntity;
import com.zcxk.util.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 报表业务处理
 * @author lihui
 * @date 2020-3-26
 */
public abstract class AbstractReportService<M extends BaseMapper<T>, T> extends ServiceImpl {

    @Autowired
    protected SceneService sceneService;

    @Autowired
    protected DeviceService deviceService;

    @Autowired
    protected DeviceParmService deviceParmService;

    @Autowired
    protected DeviceSceneMapper deviceSceneMapper;


    /**
     * 报表数据处理
     * @param queryDto
     * @param devices
     * @param reportPage
     */
    public void  reportDataHandle(ReportQueryDto queryDto, List<DeviceDto> devices, Page<ReportDto> reportPage, boolean isPipe){
        // 组装查询参数
        ReportDto reportDto = getQueryReportDto(queryDto, devices, isPipe);
        // 设置报表标题
        Map<String, Integer> mapsTitle = new LinkedHashMap<>();
        Map<String, ReportAttributeDto> mapsAttributeTitle = new LinkedHashMap<>();
        // 获取标题
        List<ReportTitleDto> reportTitleDtos = getReportTitle(reportDto, mapsTitle, mapsAttributeTitle,0);
        // 固定参数项
        List<ReportDto> reportList = findReport(reportDto);
        // 如果为空，把自定义表头返回回去
        if (CollectionUtil.isEmpty(reportPage.getRecords())){
            List<ReportDto> reportDtos = new ArrayList<>();
            ReportDto reportDto1 = new ReportDto();
            reportDto1.setReportTitle(reportTitleDtos);
            reportDtos.add(reportDto1);
            reportPage.setRecords(reportDtos);
            return;
        }
        int total = 0;
        // 单个属性值
        for (ReportDto item : reportPage.getRecords()) {
            item.setCollectDate(formateCollectDate(queryDto, item));
            item.setDeviceIds(devices);
            if (total == 0){
                item.setReportTitle(reportTitleDtos);
            }
            // 固定参数项
            calcUsage(item, reportList);
            Integer parentSceneId = item.getParentSceneId();
            item.setParentSceneId(null);
            //动态参数项
            List<ReportAttributeDto> reportAttributeDtos = this.findAttributeList(item);
            item.setParentSceneId(parentSceneId);
            Map<String, ReportAttributeDto> mapData = new LinkedHashMap<>();
            for (ReportAttributeDto itemData : reportAttributeDtos) {
                if (!mapData.containsKey(itemData.getDeviceParmId().toString())){
                    mapData.put(itemData.getDeviceParmId().toString(), itemData);
                }
            }
            // 获取统计维度的值
            item.setDataMapValues(getStatisticalDimensionsValue(mapsTitle, mapData, mapsAttributeTitle));
            total  ++;
        }
    }
    /**
     * 报表数据处理
     * @param queryDto
     * @param devices
     * @param reportPage
     */
    public void  reportHistoryDataHandle(ReportQueryDto queryDto, List<DeviceDto> devices, Page<ReportDto> reportPage, Page<MonitorDataEntity> historyData , boolean isPipe) {
        // 组装查询参数
        ReportDto reportDto = getQueryReportDto(queryDto, devices, isPipe);
        // 设置报表标题
        Map<String, Integer> mapsTitle = new LinkedHashMap<>();
        Map<String, ReportAttributeDto> mapsAttributeTitle = new LinkedHashMap<>();
        Map<String,String> mapsDeviceParm = new LinkedHashMap<>();
        // 获取标题
        List<ReportTitleDto> reportTitleDtos = getReportTitle(reportDto, mapsTitle, mapsAttributeTitle, 1);
        //按设备ID+设备属性 获取设备参数
        for(ReportAttributeDto item :mapsAttributeTitle.values()) {
            mapsDeviceParm.put(item.getDeviceId().toString() + "_" + item.getAttributeId(), item.getDeviceParmId().toString());
        }
        // 如果为空，把自定义表头返回回去
        reportPage.setSize(historyData.getSize());
        reportPage.setCurrent(historyData.getCurrent());
        reportPage.setTotal(historyData.getTotal());
        if (CollectionUtil.isEmpty(historyData.getRecords())) {
            List<ReportDto> reportDtos = new ArrayList<>();
            ReportDto reportDto1 = new ReportDto();
            reportDto1.setReportTitle(reportTitleDtos);
            reportDtos.add(reportDto1);
            reportPage.setRecords(reportDtos);
            return;
        }
        //获取场景名称
        String sceneName = "";
        if(!isPipe) {
            sceneName = sceneService.getById(queryDto.getIds()[0]).getSceneName();
        }
        int total = 0;
        // 单个属性值
        List<ReportDto> reportDtos = new LinkedList<>();


        for (MonitorDataEntity item : historyData.getRecords()) {
            ReportDto reportDtoTemp = new ReportDto();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            reportDtoTemp.setCollectDate(sdf.format(item.getCollectDate()));
            reportDtoTemp.setParentSceneName(sceneName);
            reportDtoTemp.setDeviceIds(devices);
            reportDtoTemp.setDeviceName(item.getDeviceName());
            if (total == 0) {
                reportDtoTemp.setReportTitle(reportTitleDtos);
            }
            //动态参数项
            Map<String, ReportAttributeDto> mapData = new LinkedHashMap<>();
            for (MonitorDataValueEntity itemData : item.getDataValues()) {
                if (!mapData.containsKey(itemData.getAttributeId().toString())) {
                    ReportAttributeDto itemData2 = new ReportAttributeDto();
                    itemData2.setAttributeName(itemData.getAttributeName());
                    itemData2.setLatestValue(itemData.getDataValue());
                    if (mapsDeviceParm.containsKey(item.getDeviceId().toString() + "_" + itemData.getAttributeId().toString())) {
                        mapData.put(mapsDeviceParm.get(item.getDeviceId().toString() + "_" + itemData.getAttributeId().toString()), itemData2);
                    }
                }
            }
            // 获取统计维度的值
            reportDtoTemp.setDataMapValues(getHistoryDataStatisticalDimensionsValue(mapsTitle, mapData, mapsAttributeTitle));
            reportDtos.add(reportDtoTemp);
            total++;
        }
        reportPage.setRecords(reportDtos);
    }
    /**
     * 获取统计维度的值
     * @param mapsTitle
     * @param mapData
     * @param mapsAttributeTitle
     * @return
     */
    public List<Map<String, Object>> getHistoryDataStatisticalDimensionsValue( Map<String, Integer> mapsTitle, Map<String, ReportAttributeDto> mapData,
                                                                    Map<String, ReportAttributeDto> mapsAttributeTitle) {
        // 判断是否有需要展示的字段
        boolean haveShowTitle = false;
        List<Map<String, Object>> dataMapValues = new ArrayList<>();
        for (String key : mapsTitle.keySet()) {
            ReportAttributeDto reportAttributeDto = mapsAttributeTitle.get(key);
            // 过滤掉没有开启开关的
            if (reportAttributeDto == null || !reportAttributeDto.isShowOnOff()) {
                continue;
            }
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("attributeName", mapData.get(key) == null ? key : mapData.get(key).getAttributeName());
            if (reportAttributeDto.isShowLatestValue()) {
                map.put("latestValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getLatestValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getLatestValue()));
                haveShowTitle = true;
            }
            if (!haveShowTitle) {
                continue;
            }
            haveShowTitle = false;
            dataMapValues.add(map);
        }
        return dataMapValues;
    }

    /**
     * 查询场景下的所有设备信息
     * @param ids 场景ID
     * @return
     */
    public List<DeviceDto> findDevices(Long[] ids){
        List<DeviceDto> devices = new ArrayList<>();
        DeviceDto deviceDto = new DeviceDto();
        for (Long id : ids) {
            deviceDto.setSceneIds(sceneService.findByParentIdsLike(id));
            if (deviceDto.getSceneIds().size() > 0){
                devices.addAll(deviceService.selectList(deviceDto));
            }
        }
        return devices;
    }

    /**
     * 查询场景下的所有设备信息
     * @param ids 场景ID
     * @return
     */
    public List<DeviceDto> findDevicesForReport(Long[] ids){
        List<DeviceDto> devices = new ArrayList<>();
        DeviceDto deviceDto = new DeviceDto();
        for (Long id : ids) {
            deviceDto.setSceneIds(sceneService.findByParentIdsLike(id));
            if (deviceDto.getSceneIds().size() > 0){
                devices.addAll(deviceService.selectListForReport(deviceDto));
            }
        }
        return devices;
    }

    /**
     * ids转换场景集合对象
     * @param ids 场景ID
     * @return
     */
    public List<DeviceDto> devicesToList(Long[] ids){
        List<DeviceDto> devices = new ArrayList<>();
        for (Long id : ids) {
            DeviceDto deviceDto = new DeviceDto();
            deviceDto.setId(id);
            devices.add(deviceDto);
        }
        return devices;
    }

    /**
     * 1000汇算
     * @param value
     * @return
     */
    public double doubleRoundValue(double value){
        return  (double) Math.round(value * 1000) / 1000;
    }

    /**
    * @Author lihui
    * @Description 单位除以一万,保留3位小数
    * @Date 14:18 2021/4/28
    * @Param [value]
    * @return double
    **/
    public double doubleDivideValue(double value){
        return  new BigDecimal(value).divide(BigDecimal.valueOf(10000), 3, RoundingMode.HALF_UP).doubleValue();
    }


    /**
     * 组装参数
     * @param queryDto
     * @param devices
     * @return
     */
    public ReportDto getQueryReportDto(ReportQueryDto queryDto, List<DeviceDto> devices, boolean isPipe){

        ReportDto reportDto = new ReportDto();
        reportDto.setYear(queryDto.getYear());
        reportDto.setMonth(queryDto.getMonth());
        reportDto.setDay(queryDto.getDay());
        if (isPipe){
            String name = queryDto.getType() == 1 ? "压力" : ((queryDto.getType() == 2) ?  "流量" : "水质");
            SceneIdDto sceneIdDto = deviceSceneMapper.findSceneId("管网",name, queryDto.getIds()[0]);
            if (sceneIdDto != null){
                reportDto.setSceneId(sceneIdDto.getSceneId());
            }
            reportDto.setDeviceIds(devices);
        } else {
            reportDto.setDeviceIds(devices);
            reportDto.setParentSceneIds(queryDto.getIds());
        }
        reportDto.setUserId(UserUtil.getCurrentUser().getId());
        reportDto.setStartTime(queryDto.getStartTime());
        reportDto.setEndTime(queryDto.getEndTime());
        return reportDto;
    }

    //根据设备ID查询报表测点信息
    protected ReportPageQueryDto getReportPageQuery(ReportQueryDto queryDto, List<DeviceDto> devices, boolean isPipe){
        ReportPageQueryDto dto = new ReportPageQueryDto();
        dto.setYear(queryDto.getYear());
        dto.setMonth(queryDto.getMonth());
        dto.setDay(queryDto.getDay());
        dto.setTenantId(UserUtil.getCurrentUser().getTenantId());
        dto.setDeviceIds(devices);
        // 管网和其他有点不一样
        if (isPipe){
            dto.setType(queryDto.getType());
            String name = queryDto.getType() == 1 ? "压力" : ((queryDto.getType() == 2) ?  "流量" : "水质");
            SceneIdDto sceneIdDto = deviceSceneMapper.findSceneId("管网",name, queryDto.getIds()[0]);
            if (sceneIdDto != null){
                dto.setSceneId(sceneIdDto.getSceneId());
            }
        } else {
            // 管网不需要场景
            dto.setParentSceneIds(queryDto.getIds());
        }
        dto.setStartTime(queryDto.getStartTime());
        dto.setEndTime(queryDto.getEndTime());
        return dto;
    }

    /**
     * 获取报表标题
     * @param reportDto
     * @param mapsTitle
     * @param mapsAttributeTitle
     * @return
     */
    public List<ReportTitleDto> getReportTitle(ReportDto reportDto, Map<String, Integer> mapsTitle,
                                                Map<String, ReportAttributeDto> mapsAttributeTitle,int reportType){
        // 设置标题
        Map<String, ReportTitleDto> deviceChildrenMap = new HashMap<>();
        List<ReportAttributeDto> reportAttributeDtos2 = new LinkedList<>();
        if(reportType==0) {
            reportAttributeDtos2 = deviceParmService.findAttributeNameList(reportDto);
        }else {
            reportAttributeDtos2 = deviceParmService.findHistoryDataAttributeNameList(reportDto);
        }
        int numCount = 0;
        for (ReportAttributeDto reportAttributeDto : reportAttributeDtos2) {
            // 判断开关是否开启
            if (reportAttributeDto.isShowOnOff()) {
                setReportTitle(reportAttributeDto, deviceChildrenMap);
                mapsAttributeTitle.put(reportAttributeDto.getDeviceParmId().toString(), reportAttributeDto);
            }
        }
        // 转换list返回
        List<ReportTitleDto> resultList =  deviceChildrenMap.values().stream().collect(Collectors.toList());
        for (ReportTitleDto reportTitleDto : resultList) {
            List<ReportTitleDto.DeviceChildren> childrenList = reportTitleDto.getDeviceChildren();
            for (ReportTitleDto.DeviceChildren children : childrenList) {
                mapsTitle.put(children.getParamId(), numCount++);
            }
        }
        return resultList;
    }

    /**
     * 获取统计维度的值
     * @param mapsTitle
     * @param mapData
     * @param mapsAttributeTitle
     * @return
     */
    public List<Map<String, Object>> getStatisticalDimensionsValue( Map<String, Integer> mapsTitle, Map<String, ReportAttributeDto> mapData,
                                                                    Map<String, ReportAttributeDto> mapsAttributeTitle){
        // 判断是否有需要展示的字段
        boolean haveShowTitle = false;
        List<Map<String, Object>> dataMapValues = new ArrayList<>();
        for (String key : mapsTitle.keySet()) {
            ReportAttributeDto reportAttributeDto = mapsAttributeTitle.get(key);
            // 过滤掉没有开启开关的
            if (reportAttributeDto == null || !reportAttributeDto.isShowOnOff()) {
                continue;
            }
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("attributeName", mapData.get(key) == null ? key : mapData.get(key).getAttributeName());
            if (reportAttributeDto.isShowMaxValue()) {
                map.put("maxValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getMaxValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getMaxValue()));
                haveShowTitle = true;
            }

            if (reportAttributeDto.isShowMinValue()) {
                map.put("minValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getMinValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getMinValue()));
                haveShowTitle = true;
            }
            if (reportAttributeDto.isShowSumValue()) {
                map.put("sumValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getSumValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getSumValue()));
                haveShowTitle = true;
            }
            if (reportAttributeDto.isShowLatestValue()) {
                map.put("latestValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getLatestValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getLatestValue()));
                haveShowTitle = true;
            }
            if (reportAttributeDto.isShowAvgValue()) {
                map.put("avgValue" + mapsTitle.get(key), mapData.get(key) == null || mapData.get(key).getAvgValue() == null ?
                        "-" : doubleRoundValue(mapData.get(key).getAvgValue()));
                haveShowTitle = true;
            }
            if (!haveShowTitle){
                continue;
            }
            haveShowTitle = false;
            dataMapValues.add(map);
        }
        return dataMapValues;
    }

    /**
     * 设置报表标题
     * @param v
     * @param deviceChildrenMap
     */
    private void setReportTitle(ReportAttributeDto v, Map<String, ReportTitleDto> deviceChildrenMap){
        String classify = StringUtils.isEmpty(v.getClassify()) ? v.getAttributeName() + v.getDeviceParmId() + "isNull" : v.getClassify();
        ReportTitleDto reportTitleDto = deviceChildrenMap.get(classify);
        if (reportTitleDto == null) {
            reportTitleDto = new ReportTitleDto();
            reportTitleDto.setClassify(classify);
        }
        List<ReportTitleDto.DeviceChildren> deviceChildrenList = reportTitleDto.getDeviceChildren();
        if (CollectionUtil.isEmpty(deviceChildrenList)){
            deviceChildrenList = new ArrayList<>();
        }
        String unit = StringUtils.isEmpty(v.getUnit()) ? "" : "("+v.getUnit()+")";
        ReportTitleDto.DeviceChildren deviceChildren = new ReportTitleDto.DeviceChildren();
        deviceChildren.setName(v.getAttributeName() + unit);
        deviceChildren.setParamId(v.getDeviceParmId().toString());
        // 检查是否有设置维度
        List<ReportTitleDto.StatisticalDimension> statisticalDimensions = new ArrayList<>();
        if (v.isShowMaxValue()){
            statisticalDimensions.add(getStatisticalDimension("showMaxValue", "最大值"));
        }
        if (v.isShowMinValue()){
            statisticalDimensions.add(getStatisticalDimension("showMinValue", "最小值"));
        }
        if (v.isShowSumValue()){
            statisticalDimensions.add(getStatisticalDimension("showSumValue", "合计值"));
        }
        if (v.isShowLatestValue()){
            statisticalDimensions.add(getStatisticalDimension("showLatestValue", "最新值"));
        }
        if (v.isShowAvgValue()){
            statisticalDimensions.add(getStatisticalDimension("showAvgValue", "平均值"));
        }
        // 有设置维度，添加相关信息
        if (v.isShowMaxValue() || v.isShowMinValue() || v.isShowSumValue() || v.isShowLatestValue()
                || v.isShowAvgValue()){
            deviceChildren.setDimensionList(statisticalDimensions);
            deviceChildrenList.add(deviceChildren);
            reportTitleDto.setDeviceChildren(deviceChildrenList);
            deviceChildrenMap.put(classify, reportTitleDto);
        }
    }

    /**
     * 获取统计维度
     * @param key
     * @param name
     * @return
     */
    private ReportTitleDto.StatisticalDimension getStatisticalDimension(String key, String name){
        ReportTitleDto.StatisticalDimension statisticalDimension = new ReportTitleDto.StatisticalDimension();
        statisticalDimension.setDimensionKey(key);
        statisticalDimension.setDimensionName(name);
        return statisticalDimension;
    }

    /**
    * @Author lihui
    * @Description 格式化日期
    * @Date 16:38 2021/4/15
    * @Param [queryDto, item]
    * @return java.lang.String
    **/
    private String formateCollectDate(ReportQueryDto queryDto, ReportDto item){
        // 0日 1月 2年
        if (queryDto.getReportType() == 0) {
            return item.getYear() + "-" + String.format("%02d", item.getMonth()) + "-" + String.format("%02d", item.getDay()) + " " + String.format("%02d", item.getHour()) + ":00:00";
        }
        if (queryDto.getReportType() == 1 || queryDto.getReportType() == 3) {
            return item.getYear() + "-" + String.format("%02d", item.getMonth()) + "-" + String.format("%02d", item.getDay());
        }
        if (queryDto.getReportType() == 2) {
            return item.getYear() + "-" + String.format("%02d", item.getMonth());
        }
        return null;
    }

    /**
     * 查询报表
     * @param reportDto
     * @return
     */
    abstract List<ReportDto> findReport(ReportDto reportDto);

    /**
     * 计算值
     * @param item
     * @param reportDtos
     */
    abstract void calcUsage(ReportDto item, List<ReportDto> reportDtos);

    /**
     * 查询属性
     * @param item
     * @return
     */
    abstract List<ReportAttributeDto> findAttributeList(ReportDto item);
}
