package cn.cecep.talroad.service.impl;

import cn.cecep.talroad.common.core.domain.R;
import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.common.security.utils.SecurityUtils;
import cn.cecep.talroad.domain.BasDevice;
import cn.cecep.talroad.domain.BasDeviceSite;
import cn.cecep.talroad.domain.MonElecReal;
import cn.cecep.talroad.feign.BaseRegionService;
import cn.cecep.talroad.mapper.MonElecRealMapper;
import cn.cecep.talroad.service.IBasDeviceService;
import cn.cecep.talroad.service.IBasDeviceSiteService;
import cn.cecep.talroad.service.IMonElecRealService;
import cn.cecep.talroad.vo.*;
import cn.cecep.talroad.vo.analysis.AvgValueVo;
import cn.cecep.talroad.vo.query.MonElecRealQuery;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 分表计电设备-点位对应 设备与点位为多对多的关系Service业务层处理
 *
 * @author szhpt
 * @date 2023-01-10*/
@Service
public class MonElecRealServiceImpl extends ServiceImpl<MonElecRealMapper,MonElecReal> implements IMonElecRealService
{
    @Autowired
    private IBasDeviceSiteService deviceSiteService;

    @Autowired
    private IBasDeviceService deviceService;
    @Autowired
    private MonElecRealMapper monElecRealMapper;

    @Autowired
    @Qualifier("baseRegionService")
    private BaseRegionService baseRegionService;

    public enum ParamEnum {
        activePower("总有功功率", "activePower", "千瓦"),
        reactivePower("总无功功率", "reactivePower", "千瓦"),
        activePowerQuantity("总有功电量示值", "activePowerQuantity", "安"),
        reactivePowerQuantity("总无功电量示值", "reactivePowerQuantity", "安"),
        totalPower("总功率因数", "totalPower", ""),
        currentAp("A相电流", "currentAp", "安"),
        currentBp("B相电流", "currentBp", "安"),
        currentCp("C相电流", "currentCp", "安"),
        currentSinglePhase("单相电流", "currentSinglePhase", "伏"),
        voltageAp("A相电压", "voltageAp", "伏"),
        voltageBp("B相电压", "voltageBp", "伏"),
        voltageCp("C相电压", "voltageCp", "伏");

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

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

    @Override
    public EChartsVO getCharData(MonElecRealQuery monElecReal) {
        monElecReal.setTimeSortType("asc");

        if (StringUtils.isEmpty(monElecReal.getDataTimeStart()) || StringUtils.isEmpty(monElecReal.getDataTimeEnd())) {
            throw new RuntimeException("请指定趋势图时间范围");
        }
        if (StringUtils.isEmpty(monElecReal.getDeviceSiteId())) {
            throw new RuntimeException("请指定设备点位");
        }

        // x轴数据
        List<String> xdata = DateUtils.getRangeTimes("QH", monElecReal.getDataTimeStart(), monElecReal.getDataTimeEnd());

        QueryWrapper<MonElecReal> queryWrapper = MonElecRealQuery.createLambdaQueryWrapper(monElecReal);
        List<MonElecReal> elecRealList = list(queryWrapper);

        // 以日期为key,转为map 日期格式要保证与x轴格式一样
        Map<String, MonElecReal> elecRealMap = elecRealList.stream().collect(Collectors.toMap(o -> DateUtils.parseDateToStr("yyyy-MM-dd HH:mm", o.getDataTime()), o -> o,
                // TODO 数据问题导致存在key相同的，暂取第一条
                (o1, o2) -> o1));

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

        // 若monElecReal.getPollutantVos()不为null，则只展示指定的监测参数。（若指定的参数集合是空的，则返回空数据）
        if (monElecReal.getPollutantVos() != null) {
            for (AmProblemFiVo.PollutantVo pollutantVo : monElecReal.getPollutantVos()) {
                ParamEnum paramEnum = ParamEnum.getByProp(pollutantVo.getProp());
                if (paramEnum == null) {
                    continue;
                }
                EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                ser.setName(paramEnum.label);
                ser.setUnit(paramEnum.unit);
                ser.setProp(paramEnum.prop);

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

                ser.setValues(values);
                ser.setMarkLine(pollutantVo.getMarkLine());
                ser.setMarkArea(pollutantVo.getMarkArea());
                series.add(ser);
            }
        } else { // 未指定监测参数，默认展示所有的

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

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

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

    @Override
    public List<MonElecRealVo> elecRealPage(Page<MonElecRealVo> page, MonElecRealQuery monElecReal) {
        // 若未指定点位，则按照设备或企业查询点位
        if (StringUtils.isEmpty(monElecReal.getDeviceSiteId()) || CollectionUtils.isEmpty(monElecReal.getDeviceSiteIds())) {
            List<BasDeviceSite> deviceSites;

            if (StringUtils.isNotEmpty(monElecReal.getDeviceId())) { // 按指定设备查
                deviceSites = deviceSiteService.list(new QueryWrapper<BasDeviceSite>().eq("device_id", monElecReal.getDeviceId()));
            } else if (StringUtils.isNotEmpty(monElecReal.getFactoryId())) { // 按企业查，查询企业下所有设备点位
                List<BasDevice> devices = deviceService.list(new QueryWrapper<BasDevice>()
                        .eq("factory_id", monElecReal.getFactoryId()).eq("industry", "EL")
                );
                if (devices.isEmpty()) {
                    return new ArrayList<>();
                }
                List<String> deviceIds = devices.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());

                deviceSites = deviceSiteService.list(new QueryWrapper<BasDeviceSite>().in("device_id", deviceIds));
            } else {
                throw new RuntimeException("未指定设备或企业");
            }
            if (deviceSites == null || deviceSites.isEmpty()) {
                return new ArrayList<>();
            }
            monElecReal.setDeviceSiteIds(deviceSites.stream().map(BasDeviceSite::getId).collect(Collectors.toList()));
        }
        List<MonElecRealVo> vos = baseMapper.selectElecRealPage(page, monElecReal);
        page.setRecords(vos);
        return vos;
    }

    @Override
    public List<MonElecRealSiteVo> selectLatestMonElecRealByParams(String industry,String deviceType,Date lastDate,Date currDate) {
        return monElecRealMapper.selectLatestMonElecRealByParams(industry,deviceType , lastDate, currDate);
    }

    @Override
    public EChartsVO getKwhChartData(String startTime, String endTime) {
        // x轴数据
        List<String> xdata = DateUtils.getRangeTimes("QH", startTime, endTime);

        EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
        ser.setName("用电量");
        ser.setValues(new ArrayList<>());

        EChartsVO chartsVO = new EChartsVO();
        chartsVO.setXData(xdata);
        chartsVO.setSeries(Arrays.asList(ser));

        String userRegionCode = SecurityUtils.getLoginUser().getSysUser().getRegionCode();

        if (StringUtils.isEmpty(userRegionCode)) {
            return chartsVO;
        }
        R<List<String>> r = baseRegionService.getRegionCodeListChild(userRegionCode);
        if (r.getCode() == R.SUCCESS) {
            List<Map<String, Object>> dataList = monElecRealMapper.selectKwhChartData(startTime, endTime, r.getData());

            // 以日期为key,转为map 日期格式要保证与x轴格式一样
            Map<String, Object> elecRealMap = dataList.stream().collect(Collectors.toMap(o -> (String) o.get("dataTime"), o -> o.get("activePowerQuantity")));

            ser.setValues(xdata.stream().map(elecRealMap::get).collect(Collectors.toList()));
        }
        return chartsVO;
    }

    @Override
    public List<MonElecRealVo.KwhMonitorVo> getKwhMonitor(String type, String startTime, String endTime) {
        String userRegionCode = SecurityUtils.getLoginUser().getSysUser().getRegionCode();

        if (StringUtils.isEmpty(userRegionCode)) {
            return new ArrayList<>();
        }
        R<List<String>> r = baseRegionService.getRegionCodeListChild(userRegionCode);
        if (r.getCode() == R.SUCCESS) {
            List<MonElecRealVo.KwhMonitorVo> vos = monElecRealMapper.selectKwhMonitor(type, startTime, endTime, r.getData());
            if (vos.isEmpty()) {
                return vos;
            }

            // 获取到用电量最大的
            MonElecRealVo.KwhMonitorVo max = vos.stream().max(Comparator.comparing(MonElecRealVo.KwhMonitorVo::getKwh)).get();

            for (MonElecRealVo.KwhMonitorVo vo : vos) {
                // 计算占比
                vo.setPercentage(vo.getKwh().multiply(new BigDecimal(100)).divide(max.getKwh(), 2, BigDecimal.ROUND_HALF_UP)
                        .stripTrailingZeros().toPlainString());
            }
            return vos;
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * 获取分表计电点位图表数据，将多个监测点位的参数合并到一个图表上
     */
    @Override
    public EChartsVO getCharData(List<BasDeviceSiteVo.SimpleVo> simpleVos, String startTime, String endTime) {
        // x轴数据
        List<String> xdata = DateUtils.getRangeTimes("QH", startTime, endTime);

        EChartsVO chartsVO = new EChartsVO();
        chartsVO.setXData(xdata);

        if (simpleVos == null || simpleVos.isEmpty() || StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) {
            return chartsVO;
        }

        // 查询指定时间范围内，指定点位的监测数据
        MonElecRealQuery query = new MonElecRealQuery();
        query.setDataTimeStart(startTime);
        query.setDataTimeEnd(endTime);
        query.setDeviceSiteIds(simpleVos.stream().map(BasDeviceSiteVo.SimpleVo::getId).collect(Collectors.toList()));

        QueryWrapper<MonElecReal> queryWrapper = MonElecRealQuery.createLambdaQueryWrapper(query);
        List<MonElecReal> elecRealList = list(queryWrapper);

        // 以日期和点位id为key,转为map 日期格式要保证与x轴格式一样
        Map<String, MonElecReal> elecRealMap = elecRealList.stream().collect(
                Collectors.toMap(o -> o.getDeviceSiteId() + "_" + DateUtils.parseDateToStr("yyyy-MM-dd HH:mm", o.getDataTime()), o -> o,
                // TODO 数据问题导致存在key相同的，暂取第一条
                (o1, o2) -> o1));

        // 汇总所有点位的参数
        Set<String> paramSet = new HashSet<>();
        for (BasDeviceSiteVo.SimpleVo simpleVo : simpleVos) {
            for (AmProblemFiVo.PollutantVo pollutantVo : simpleVo.getPollutantVos()) {
                paramSet.add(pollutantVo.getProp());
            }
        }

        List<EChartsVO.SeriesObj> series = new ArrayList<>();

        for (BasDeviceSiteVo.SimpleVo simpleVo : simpleVos) {
            for (AmProblemFiVo.PollutantVo pollutantVo : simpleVo.getPollutantVos()) {
                ParamEnum paramEnum = ParamEnum.getByProp(pollutantVo.getProp());
                if (paramEnum == null) {
                    continue;
                }
                EChartsVO.SeriesObj ser = new EChartsVO.SeriesObj();
                ser.setUnit(paramEnum.unit);
                ser.setProp(paramEnum.prop);

                // 若只有一个点位，系列名称展示参数名
                if (simpleVos.size() == 1) {
                    ser.setName(paramEnum.label);
                } else if (paramSet.size() == 1) { // 若只有一个参数，系列名称展示点位名称
                    ser.setName(simpleVo.getName());
                } else {
                    ser.setName(simpleVo.getName() + "-" + paramEnum.label);
                }

                // 遍历x轴数据，保证x轴与系列数据一一对应
                List<Object> values = xdata.stream().map(o -> {
                    MonElecReal monReal = elecRealMap.get(simpleVo.getId() + "_" + o);
                    if (monReal == null) {
                        return null;
                    }
                    return BeanUtil.getProperty(monReal, paramEnum.prop);
                }).collect(Collectors.toList());

                ser.setValues(values);
                series.add(ser);
            }
        }
        chartsVO.setSeries(series);
        // 若只有一个点位，图表名称展示点位名称
        if (simpleVos.size() == 1) {
            chartsVO.setName(simpleVos.get(0).getName());
        } else if (paramSet.size() == 1) { // 若只有一个参数，系列名称展示参数名称
            ParamEnum paramEnum = ParamEnum.getByProp(paramSet.stream().findAny().get());
            chartsVO.setName(paramEnum != null ? paramEnum.label : null);
        }
        return chartsVO;
    }

    @Override
    public List<AvgValueVo> selectLastDayData(DateTime queryBeginTime, DateTime queryEndTime,Integer pageSize,Integer startCount) {
        return monElecRealMapper.selectLastDayData(queryBeginTime,queryEndTime,pageSize,startCount);
    }

}
