package cn.cecep.talroad.service.impl;

import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.common.core.utils.StringUtils;
import cn.cecep.talroad.domain.PcOutlet;
import cn.cecep.talroad.domain.SEnvGasMonReal;
import cn.cecep.talroad.domain.SEnvWaterMonHour;
import cn.cecep.talroad.domain.SEnvWaterMonReal;
import cn.cecep.talroad.domain.analysis.SRaWaterPermittedOnline;
import cn.cecep.talroad.enums.TimeTypeEnum;
import cn.cecep.talroad.mapper.PcOutletMapper;
import cn.cecep.talroad.mapper.SEnvWaterMonHourMapper;
import cn.cecep.talroad.service.ISEnvWaterMonHourService;
import cn.cecep.talroad.vo.AmProblemFiVo;
import cn.cecep.talroad.vo.EChartsVO;
import cn.cecep.talroad.vo.SEnvWaterMonHourVo;
import cn.cecep.talroad.vo.analysis.FactoryOutletStaticsVo;
import cn.cecep.talroad.vo.query.SEnvWaterMonHourQuery;
import cn.cecep.talroad.vo.query.analysis.SRaWaterPermittedOnlineQuery;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.*;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * 废水监测数据Service业务层处理
 *
 * @author szhpt
 * @date 2023-01-10*/
@Service
public class SEnvWaterMonHourServiceImpl extends ServiceImpl<SEnvWaterMonHourMapper,SEnvWaterMonHour> implements ISEnvWaterMonHourService
{
    @Autowired
    private SEnvWaterMonHourMapper sEnvWaterMonHourMapper;

    @Autowired
    private PcOutletMapper pcOutletMapper;

    /**
     * 查询废水监测数据列表
     *
     * @param sEnvWaterMonReal 废水监测数据
     * @return 废水监测数据
     */
    @Override
    public List<SEnvWaterMonHourVo> selectSEnvWaterMonHourList(SEnvWaterMonHourQuery sEnvWaterMonReal)
    {
        String format;
        switch (sEnvWaterMonReal.getDataType()) {
            default:
                format = "yyyy-MM-dd HH:mm:ss";
                break;
            case "D":
                // 天
                format = "yyyy-MM-dd";
                break;
            case "H":
                // 小时
                format = "yyyy-MM-dd HH";
                break;
            case "M":
                // 分钟
                format = "yyyy-MM-dd HH:mm";
                break;
        }

        DateTime monitoringStartTime = DateUtil.parse(sEnvWaterMonReal.getMonitoringStartTime(), format);
        DateTime monitoringEndTime = DateUtil.parse(sEnvWaterMonReal.getMonitoringEndTime(), format);
        sEnvWaterMonReal.setMonitoringStartTime(monitoringStartTime.toString());
        sEnvWaterMonReal.setMonitoringEndTime(monitoringEndTime.toString());

        return sEnvWaterMonHourMapper.selectSEnvWaterMonHourList(null, sEnvWaterMonReal, new QueryWrapper<SEnvWaterMonHour>().orderByDesc("monitoring_time"));
    }

    /**
     * 查询废水监测数据分页数据
     *
     * @param sEnvWaterMonReal 废水监测数据
     * @return 废水监测数据
     */
    @Override
    public IPage<SEnvWaterMonHourVo> selectSEnvWaterMonHourPage(SEnvWaterMonHourQuery sEnvWaterMonReal) {
        Page<SEnvWaterMonHourVo> page = new Page<>(sEnvWaterMonReal.getPageNum(), sEnvWaterMonReal.getPageSize());

        String format;
        switch (sEnvWaterMonReal.getDataType()) {
            default:
                format = "yyyy-MM-dd HH:mm:ss";
                break;
            case "D":
                // 天
                format = "yyyy-MM-dd";
                break;
            case "H":
                // 小时
                format = "yyyy-MM-dd HH";
                break;
            case "M":
                // 分钟
                format = "yyyy-MM-dd HH:mm";
                break;
        }

        DateTime monitoringStartTime = DateUtil.parse(sEnvWaterMonReal.getMonitoringStartTime(), format);
        DateTime monitoringEndTime = DateUtil.parse(sEnvWaterMonReal.getMonitoringEndTime(), format);
        sEnvWaterMonReal.setMonitoringStartTime(monitoringStartTime.toString());
        sEnvWaterMonReal.setMonitoringEndTime(monitoringEndTime.toString());

        List<SEnvWaterMonHourVo> sEnvWaterMonReals = sEnvWaterMonHourMapper.selectSEnvWaterMonHourList(page, sEnvWaterMonReal, new QueryWrapper<SEnvWaterMonHour>().orderByDesc("monitoring_time"));
        page.setRecords(sEnvWaterMonReals);
        return page;
    }

    /**
     * 废水在线监测所有污染物
     */
    public enum PollutantEnum {
        codConcentration("COD浓度", "codConcentration", "mg/L", "W01018", "codFlag"),
        ammoniaNitrogen("氨氮浓度", "ammoniaNitrogen", "mg/m³", "W21003", "ammoniaNitrogenFlag"),
        nitrogenConcentration("总氮浓度", "nitrogenConcentration", "mg/m³", "W21001", "nitrogenConcentrationFlag"),
        phosphorusConcentration("总磷浓度", "phosphorusConcentration", "mg/m³", "W21011", "phosphorusConcentrationFlag"),
        phValue("PH值", "phValue", "", "W01001", "phValueFlag"),
        wastewaterFlow("废水流量", "wastewaterFlow", "m³/s", null, null);

        public final String label; // 名称
        public final String prop; // 取值属性名
        public final String unit; // 单位
        public final String code; // 编码
        public final String flag; // 标记字段对应属性名

        PollutantEnum(String label, String prop, String unit, String code, String flag) {
            this.label = label;
            this.prop = prop;
            this.unit = unit;
            this.code = code;
            this.flag = flag;
        }
        public static PollutantEnum getByProp(String prop) {
            for (PollutantEnum value : values()) {
                if (value.prop.equals(prop)) {
                    return value;
                }
            }
            return null;
        }
        public static PollutantEnum getByCode(String code) {
            for (PollutantEnum value : values()) {
                if (value != null && value.prop.equals(code)) {
                    return value;
                }
            }
            return null;
        }
    }
    /**
     * 废水在线监测所有污染物
     */
    public enum PollutantEnumEmission {
        codEmission("化学需氧量排放量", "codEmission", "m³", "W01018"),
        ammoniaNitrogenEmission("氨氮排放量", "ammoniaNitrogenEmission", "m³", "W21003"),
        nitrogenConcentrationEmission("总氮排放量", "nitrogenConcentrationEmission", "m³", "W21001"),
        phosphorusConcentrationEmission("总磷排放量", "phosphorusConcentrationEmission", "m³", "W21011"),
        sewageEmission("污水排放量", "sewageEmission", "m³", null);

        public final String label; // 名称
        public final String prop; // 取值属性名
        public final String unit; // 单位
        public final String code; // 编码

        PollutantEnumEmission(String label, String prop, String unit, String code) {
            this.label = label;
            this.prop = prop;
            this.unit = unit;
            this.code = code;
        }
        public static PollutantEnumEmission getByProp(String prop) {
            for (PollutantEnumEmission value : values()) {
                if (value.prop.equals(prop)) {
                    return value;
                }
            }
            return null;
        }
        public static PollutantEnumEmission getByCode(String code) {
            for (PollutantEnumEmission value : values()) {
                if (value != null && value.prop.equals(code)) {
                    return value;
                }
            }
            return null;
        }
    }

    @Override
    public EChartsVO getCharData(SEnvWaterMonHourQuery query) {
        // 结束日期格式补全，解决查询出的数据不包含结束日期的情况
        if (StringUtils.isNotEmpty(query.getMonitoringEndTime())) {
            Date date = DateUtils.parseDate(query.getMonitoringEndTime());
            query.setMonitoringEndTime(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date));
        }

        List<SEnvWaterMonHour> waterMonReals = sEnvWaterMonHourMapper.selectList(SEnvWaterMonHourQuery.createLambdaQueryWrapper(query).orderByAsc("monitoring_time"));

        // x轴数据
        List<String> xdata = DateUtils.getRangeTimes(query.getDataType(), query.getMonitoringStartTime(), query.getMonitoringEndTime());

        // 以日期为key,转为map 日期格式要保证与x轴格式一样
        Map<String, SEnvWaterMonHour> waterMonRealMap = waterMonReals.stream().collect(Collectors.toMap(o -> {
            Date date = DateUtils.parseDate(o.getMonitoringTime());
            if (TimeTypeEnum.MONTH.getCode().equals(o.getDataType())) {
                return DateUtils.parseDateToStr("yyyy-MM-dd HH:mm", date);
            } else if (TimeTypeEnum.HOUR.getCode().equals(o.getDataType())) {
                return DateUtils.parseDateToStr("yyyy-MM-dd HH", date);
            } else {
                return DateUtils.parseDateToStr("yyyy-MM-dd", date);
            }
        }, o -> o,
                // TODO 数据问题导致存在key相同的，暂取第一条
                (o1, o2) -> o1));

        /*
            将查询到的监测数据转为曲线图要求的格式
         */
        // 系列
        List<EChartsVO.SeriesObj> series = new ArrayList<>();

        // 若query.getPollutantVos()不为null，则只展示指定的监测参数。（若指定的参数集合是空的，则返回空数据）
        if (query.getPollutantVos() != null) {
            // 去除掉prop属性为空的，并且通过map去重
            LinkedHashMap<String, AmProblemFiVo.PollutantVo> pollutantVoLinkedHashMap = query.getPollutantVos().stream()
                    .filter(o -> o.getProp() != null)
                    .collect(Collectors.toMap(o -> o.getProp(), o -> o,
                            // 保留第一个
                            (o1, o2) -> o1,
                            // 保持顺序不变
                            LinkedHashMap::new));
            query.setPollutantVos(new ArrayList<>(pollutantVoLinkedHashMap.values()));

            for (AmProblemFiVo.PollutantVo pollutantVo : query.getPollutantVos()) {
                PollutantEnum pollutantEnum = PollutantEnum.getByProp(pollutantVo.getProp());
                if (pollutantEnum == null) { // 无效的污染物，忽略
                    continue;
                }
                EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                ser.setName(pollutantEnum.label);
                ser.setUnit(pollutantEnum.unit);
                ser.setProp(pollutantEnum.prop);

                // 填充曲线图系列值。遍历x轴数据，保证x轴与系列数据一一对应
                List<Object> values = xdata.stream().map(o -> {
                    SEnvWaterMonHour monReal = waterMonRealMap.get(o);
                    if (monReal == null) {
                        return null;
                    }
                    return BeanUtil.getProperty(monReal, ser.getProp());
                }).collect(Collectors.toList());
                ser.setValues(values);

                ser.setMarkLine(pollutantVo.getMarkLine());
                ser.setMarkArea(pollutantVo.getMarkArea());

                series.add(ser);

                List<EChartsVO.SeriesObj> limitSeries = getLimitSeriesObj(pollutantVo, pollutantEnum, xdata);
                if (limitSeries != null) {
                    // 由于当前遍历的监测参数存在上下限系列，设置系列类型为“problem”，前端会通过该属性特殊渲染
                    ser.setSeriesType("problem");
                    series.addAll(limitSeries);
                }

                List<EChartsVO.SeriesObj> flagSeriesObj = getFlagSeriesObj(pollutantVo, xdata, waterMonRealMap, pollutantEnum);
                if (flagSeriesObj != null) {
                    series.addAll(flagSeriesObj);
                }
            }
        } else { // 未指定监测参数，默认展示所有的

            for (PollutantEnum pollutantEnum : PollutantEnum.values()) {
                EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                ser.setName(pollutantEnum.label);
                ser.setUnit(pollutantEnum.unit);
                ser.setProp(pollutantEnum.prop);

                // 遍历x轴数据，保证x轴与系列数据一一对应
                List<Object> values = xdata.stream().map(o -> {
                    SEnvWaterMonHour monReal = waterMonRealMap.get(o);
                    if (monReal == null) {
                        return null;
                    }
                    return BeanUtil.getProperty(monReal, ser.getProp());
                }).collect(Collectors.toList());
                ser.setValues(values);

                series.add(ser);
            }

        }

        EChartsVO chartsVO = new EChartsVO();
        chartsVO.setXData(xdata);
        chartsVO.setSeries(series);
        return chartsVO;
    }

    /**
     * 若监测物是问题参数，需要生成状态标记相关系列
     */
    private List<EChartsVO.SeriesObj> getFlagSeriesObj(
            AmProblemFiVo.PollutantVo pollutantVo,
            List<String> xdata,
            Map<String, SEnvWaterMonHour> waterMonRealMap,
            PollutantEnum pollutantEnum
    ) {
        if (pollutantVo.getProblemParam() == null || !pollutantVo.getProblemParam()) {
            return null;
        }
        Map<String, EChartsVO.SeriesObj> dataFlagSerMap = new HashMap<>();
        Map<String, EChartsVO.SeriesObj> paramFlagSerMap = new HashMap<>();

        // 计算x轴时间范围内有多少种dataFlag标记和有多少参数标记，并创建对应的系列对象
        for (String time : xdata) {
            SEnvWaterMonHour real = waterMonRealMap.get(time);
            if (real == null) {
                continue;
            }
            SEnvGasMonReal.DataFlagEnum dataFlagEnum = SEnvGasMonReal.DataFlagEnum.getByEnumName(real.getDataFlag());

            // 若参数标记与dataFlag标记同时存在，优先取dataFlag标记
            if (dataFlagEnum != null) {
                if (!dataFlagSerMap.containsKey(dataFlagEnum.name())) {
                    EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                    ser.setName(dataFlagEnum.label + "（标记）");
                    ser.setProp(dataFlagEnum.name());
                    ser.setValues(new ArrayList<>());
                    dataFlagSerMap.put(dataFlagEnum.name(), ser);
                }
            } else {
                // 获取参数标记
                String propflag = BeanUtil.getProperty(real, pollutantEnum.flag);
                SEnvGasMonReal.ParamFlagEnum paramFlagEnum = SEnvGasMonReal.ParamFlagEnum.getByEnumName(propflag);

                if (paramFlagEnum != null && paramFlagEnum.show) {
                    if (!paramFlagSerMap.containsKey(paramFlagEnum.name())) {
                        EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                        ser.setName(paramFlagEnum.label + "（标记）");
                        ser.setProp(paramFlagEnum.name());
                        ser.setValues(new ArrayList<>());
                        paramFlagSerMap.put(paramFlagEnum.name(), ser);
                    }
                }
            }
        }

        // 所有的状态标记系列值保持一样
        final String Flag_Value = "1";

        // 填充系列值
        if (dataFlagSerMap.isEmpty() && paramFlagSerMap.isEmpty()) {
            return null;
        }

        // 遍历x轴，保证系列值与x轴一一对应
        for (int i = 0; i < xdata.size(); i++) {
            String time = xdata.get(i);

            // 默认所有系列填充null值
            for (EChartsVO.SeriesObj ser : dataFlagSerMap.values()) {
                ser.getValues().add(null);
            }
            for (EChartsVO.SeriesObj ser : paramFlagSerMap.values()) {
                ser.getValues().add(null);
            }

            SEnvWaterMonHour real = waterMonRealMap.get(time);
            if (real == null) {
                continue;
            }
            SEnvGasMonReal.DataFlagEnum dataFlagEnum = SEnvGasMonReal.DataFlagEnum.getByEnumName(real.getDataFlag());

            if (dataFlagEnum != null) {
                dataFlagSerMap.get(dataFlagEnum.name()).getValues().set(i, Flag_Value);
            } else {
                // 获取参数标记
                String propflag = BeanUtil.getProperty(real, pollutantEnum.flag);
                SEnvGasMonReal.ParamFlagEnum paramFlagEnum = SEnvGasMonReal.ParamFlagEnum.getByEnumName(propflag);

                if (paramFlagEnum != null && paramFlagEnum.show) {
                    paramFlagSerMap.get(paramFlagEnum.name()).getValues().set(i, Flag_Value);
                }
            }
        }
        List<EChartsVO.SeriesObj> serList = new ArrayList<>();
        serList.addAll(new ArrayList<>(dataFlagSerMap.values()));
        serList.addAll(new ArrayList<>(paramFlagSerMap.values()));
        return serList;
    }

    /**
     * 若监测物指定了上下限，还需要生成上下限的曲线图
     */
    private List<EChartsVO.SeriesObj> getLimitSeriesObj(AmProblemFiVo.PollutantVo pollutantVo, PollutantEnum pollutantEnum, List<String> xdata) {
        if (pollutantVo.getPollutantLimitMap() == null) {
            return null;
        }
        EChartsVO.SeriesObj lowerLimit = new EChartsVO.SeriesObj();
        lowerLimit.setName(pollutantEnum.label + "下限");
        lowerLimit.setUnit(pollutantEnum.unit);
        lowerLimit.setProp("lowerLimit");
        lowerLimit.setSeriesType("lowerLimit");

        EChartsVO.SeriesObj upperLimit = new EChartsVO.SeriesObj();
        upperLimit.setName(pollutantEnum.label + "上限");
        upperLimit.setUnit(pollutantEnum.unit);
        upperLimit.setProp("upperLimit");
        upperLimit.setSeriesType("upperLimit");

        List<EChartsVO.SeriesObj> seriesObjs = Arrays.asList(lowerLimit, upperLimit);

        for (EChartsVO.SeriesObj seriesObj : seriesObjs) {
            // 填充曲线图系列值。遍历x轴数据，保证x轴与系列数据一一对应
            List<Object> values2 = xdata.stream().map(o -> {
                AmProblemFiVo.PollutantLimitVo pollutantLimitVo = pollutantVo.getPollutantLimitMap().get(o);
                if (pollutantLimitVo == null) {
                    return null;
                }
                return BeanUtil.getProperty(pollutantLimitVo, seriesObj.getProp());
            }).collect(Collectors.toList());
            seriesObj.setValues(values2);
        }
        return seriesObjs;
    }

    @Override
    public List<EChartsVO> getCharDataList(SEnvWaterMonHourQuery sEnvWaterMonReal) {
        List<PcOutlet> children = pcOutletMapper.selectList(new QueryWrapper<PcOutlet>()
                .eq("parent_outlet_id", sEnvWaterMonReal.getOutletId())
                .eq("pollutant_type", "4")
        );

        return children.stream().map(child -> {
            sEnvWaterMonReal.setOutletId(child.getId());

            EChartsVO vo = getCharData(sEnvWaterMonReal);
            vo.setName(child.getMonitoringPointName());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<SEnvWaterMonHour> selectLastDayData(String pollutantOutletType, Date lastDayBegin, Date lastDayEnd) {
        return sEnvWaterMonHourMapper.selectLastDayData(pollutantOutletType,lastDayBegin,lastDayEnd);
    }

    @Override
    public List<SEnvWaterMonHour> selectAvgDataForDay(String dateStr) {
        return sEnvWaterMonHourMapper.selectAvgDataForDay(dateStr);
    }

    @Override
    public List<SEnvWaterMonHour> selectSRaWaterPermittedOnlineListBy(SRaWaterPermittedOnlineQuery lambdaQueryWrapper) {
        return sEnvWaterMonHourMapper.selectSRaWaterPermittedOnlineListBy(lambdaQueryWrapper);
    }

    @Override
    public List<SEnvWaterMonHour> selectSRaWaterSourceListBy(SRaWaterPermittedOnlineQuery lambdaQueryWrapper) {
        return sEnvWaterMonHourMapper.selectSRaWaterSourceListBy(lambdaQueryWrapper);
    }

    @Override
    public List<FactoryOutletStaticsVo> selectLastDayCountData(DateTime queryBeginTime, DateTime queryEndTime) {
        return sEnvWaterMonHourMapper.selectLastDayCountData(queryBeginTime,queryEndTime);
    }

    @Override
    public  List<SEnvWaterMonHour> selectManualSRaWaterPermittedOnlineListBy(SRaWaterPermittedOnlineQuery lambdaQueryWrapper) {
        return sEnvWaterMonHourMapper.selectManualSRaWaterPermittedOnlineListBy(lambdaQueryWrapper);
    }

    @Override
    public List<SEnvWaterMonHour> selectManualSRaWaterSourceListBy(SRaWaterPermittedOnlineQuery lambdaQueryWrapper) {
        return sEnvWaterMonHourMapper.selectManualSRaWaterSourceListBy(lambdaQueryWrapper);
    }
}
