package cn.cecep.talroad.data.analyse.task.service;

import cn.cecep.talroad.data.analyse.task.anomaly.analysis.DataAnomalyAbstractService;
import cn.cecep.talroad.data.analyse.task.service.TaskService;
import cn.cecep.talroad.domain.AmProblemFi;
import cn.cecep.talroad.domain.OdsAirRegionAqiHourI;
import cn.cecep.talroad.domain.SStationTypeDict;
import cn.cecep.talroad.domain.TCodStation;
import cn.cecep.talroad.domain.TCodStationDataAir;
import cn.cecep.talroad.mapper.AmProblemFiMapper;
import cn.cecep.talroad.mapper.SStationTypeDictMapper;
import cn.cecep.talroad.mapper.TCodStationDataAirMapper;
import cn.cecep.talroad.mapper.TCodStationMapper;
import cn.cecep.talroad.service.IOdsAirRegionAqiHourIService;
import cn.cecep.talroad.util.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class SiteMonitoringService implements TaskService {

    @Autowired
    private TCodStationMapper codStationMapper;

    @Autowired
    private SStationTypeDictMapper stationTypeDictMapper;

    @Autowired
    private TCodStationDataAirMapper codStationDataAirMapper;

    @Autowired
    private IOdsAirRegionAqiHourIService odsAirRegionAqiHourIService;

    @Autowired
    private AmProblemFiMapper amProblemFiMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    public String[] numberTypeCityIds = {"1","2"};

    public String[] limitTypeCityIds = {"10"};

    public static final String redisKey = "frequency:alarm:";

    @Override
    public Object mainProcess(Object dto) {
        //限值预警
        limitJudgment();
        //次数预警
        FrequencyAlarm();
        return null;
    }


    public void limitJudgment(){
        List<SStationTypeDict> stationType = getStationType(limitTypeCityIds);
        for (SStationTypeDict sStationTypeDict : stationType) {
            // 获取分类站点
            List<TCodStation> codStations = getStation(sStationTypeDict);
            if (CollectionUtils.isNotEmpty(codStations)){
                // 获取站点数据
                List<TCodStationDataAir> codeStationDataAir = getCodeStationDataAir(codStations);
                if (CollectionUtils.isNotEmpty(codeStationDataAir)){
                    // 分组站点数据
                    Map<String, List<TCodStationDataAir>> airMap = codeStationDataAir.stream().collect(Collectors.groupingBy(TCodStationDataAir::getStationId));
                    // 站点转map 方便通过id 获取
                    Map<String, TCodStation> tCodStationMap = codStations.stream().collect(Collectors.toMap(TCodStation::getId, Function.identity()));

                    for (Map.Entry<String, List<TCodStationDataAir>> listEntry : airMap.entrySet()) {

                        // 获取分类字段
                        List<String> paramNames = Arrays.stream(LimitValueEnum.values()).map(LimitValueEnum::getParamName).collect(Collectors.toList());

                        //获取该字段所有数据
                        Map<String, List<Object>> columnParamValue = DataAnomalyAbstractService.getColumnParamValue(listEntry.getValue(), paramNames);

                        // 分析每个字段的预警  并添加预警信息
                        for (Map.Entry<String, List<Object>> stringListEntry : columnParamValue.entrySet()) {

                            List<BigDecimal> bigDecimals = stringListEntry.getValue().stream().map(Object::toString).map(BigDecimal::new).collect(Collectors.toList());
                            if (CollectionUtils.isNotEmpty(bigDecimals)){

                                BigDecimal avg = bigDecimals.stream().reduce(BigDecimal.ZERO, BigDecimal :: add).divide(BigDecimal.valueOf(bigDecimals.size()), 2, BigDecimal.ROUND_HALF_UP);
                                LimitValueEnum limitValueEnum = LimitValueEnum.geLimitValueByParam(stringListEntry.getKey());
                                if (limitValueEnum!=null){
                                    BigDecimal value = limitValueEnum.getValue();
                                    if (avg.doubleValue()>=value.doubleValue()){
                                        TCodStation tCodStation = tCodStationMap.get(listEntry.getKey());
                                        String message = getLimitAlarm(tCodStation.getStationName(), limitValueEnum.getParamName(), DateUtil.formatDate(getStartTime(), "yyyyMMddHH"), avg, limitValueEnum.getValue());
                                        AmProblemFi amProblemFi = getAmProblemFi(tCodStation, message, limitValueEnum.paramName);
                                        amProblemFiMapper.insert(amProblemFi);
                                    }
                                }else{
                                    log.warn("param  {} in LimitValueEnum enum not be found",stringListEntry.getKey());
                                }
                            }
                        }

                    }


                }
            }
        }

    }

    public void FrequencyAlarm(){
        List<SStationTypeDict> stationType = getStationType(numberTypeCityIds);
        List<OdsAirRegionAqiHourI> cityAirRegionHour = getCityAirRegionHour();
        for (SStationTypeDict sStationTypeDict : stationType) {
            // 获取分类站点
            List<TCodStation> codStations = getStation(sStationTypeDict);
            if (CollectionUtils.isNotEmpty(codStations)){
                // 获取站点数据
                List<TCodStationDataAir> codeStationDataAir = getCodeStationDataAir(codStations);
                if (CollectionUtils.isNotEmpty(codeStationDataAir)){
                    // 分组站点数据
                    Map<String, List<TCodStationDataAir>> airMap = codeStationDataAir.stream().collect(Collectors.groupingBy(TCodStationDataAir::getStationId));
                    // 站点转map 方便通过id 获取
                    Map<String, TCodStation> tCodStationMap = codStations.stream().collect(Collectors.toMap(TCodStation::getId, Function.identity()));

                    for (Map.Entry<String, List<TCodStationDataAir>> listEntry : airMap.entrySet()) {

                        // 获取分类字段
                        List<String> paramNames = Arrays.stream(sStationTypeDict.getChartPollutant().split(",")).collect(Collectors.toList());

                        //获取该字段所有数据
                        Map<String, List<Object>> columnParamValue = DataAnomalyAbstractService.getColumnParamValue(listEntry.getValue(), paramNames);
                        Map<String, List<Object>> cityHourCity = DataAnomalyAbstractService.getColumnParamValue(cityAirRegionHour, paramNames);

                        // 分析每个字段的预警  并添加预警信息
                        for (Map.Entry<String, List<Object>> stringListEntry : columnParamValue.entrySet()) {

                            List<BigDecimal> bigDecimals = stringListEntry.getValue().stream().map(Object::toString).map(BigDecimal::new).collect(Collectors.toList());
                            List<Object> objects = cityHourCity.get(stringListEntry.getKey());
                            if (CollectionUtils.isNotEmpty(objects)){
                                List<BigDecimal> bigDecimalList = objects.stream().map(Object::toString).map(BigDecimal::new).collect(Collectors.toList());
                                if (CollectionUtils.isNotEmpty(bigDecimals)){
                                    TCodStation tCodStation = tCodStationMap.get(listEntry.getKey());
                                    BigDecimal cityAvg = bigDecimalList.stream().reduce(BigDecimal.ZERO, BigDecimal :: add).divide(BigDecimal.valueOf(bigDecimals.size()), 2, BigDecimal.ROUND_HALF_UP);
                                    BigDecimal avg = bigDecimals.stream().reduce(BigDecimal.ZERO, BigDecimal :: add).divide(BigDecimal.valueOf(bigDecimals.size()), 2, BigDecimal.ROUND_HALF_UP);
                                    Object o = redisTemplate.opsForValue().get("redisKey" + tCodStation.getId());
                                    if (o==null){
                                        // 进行第一次报警
                                        BigDecimal multiply = cityAvg.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("30"));
                                        if (avg.doubleValue()>=multiply.doubleValue()){
                                            String message = getOneAlarm(tCodStation.getStationName(), stringListEntry.getKey(), DateUtil.formatDate(getStartTime(), "yyyyMMddHH"), avg);
                                            AmProblemFi amProblemFi = getAmProblemFi(tCodStation, message, stringListEntry.getKey());
                                            amProblemFiMapper.insert(amProblemFi);
                                        }
                                        String format = DateUtil.format(DateUtil.getDateHour(2), "yyyy-MM-dd HH:mm:ss");

                                        redisTemplate.opsForValue().set("redisKey" + tCodStation.getId(),format,60*60*2+60*30, TimeUnit.SECONDS);
                                    }
                                    String dateString = o.toString();
                                    Date parse = DateUtil.parse(dateString, "yyyy-MM-dd HH::mm:ss");
                                    if (parse.getTime()<parse.getTime()){

                                        //进行第二次报警
                                        BigDecimal multiply = cityAvg.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("15"));
                                        if (avg.doubleValue()>=multiply.doubleValue()){
                                            String message = getTwoAlarm(tCodStation.getStationName(), stringListEntry.getKey(), DateUtil.formatDate(getStartTime(), "yyyyMMddHH"), avg);
                                            AmProblemFi amProblemFi = getAmProblemFi(tCodStation, message, stringListEntry.getKey());
                                            amProblemFiMapper.insert(amProblemFi);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     * 获取市数据
     * @return
     */
    public List<OdsAirRegionAqiHourI> getCityAirRegionHour(){
        LambdaQueryWrapper<OdsAirRegionAqiHourI> odsAirRegionAqiHourILambdaQueryWrapper = new QueryWrapper<OdsAirRegionAqiHourI>().lambda()
                .ge(OdsAirRegionAqiHourI::getDatatime, DateUtil.format(getStartTime(), "yyyy-MM-dd HH:mm:ss"))
                .le(OdsAirRegionAqiHourI::getDatatime, DateUtil.format(getEndTime(), "yyyy-MM-dd HH:mm:ss"));
        return odsAirRegionAqiHourIService.list(odsAirRegionAqiHourILambdaQueryWrapper);
    }

    /**
     * 获取站点信息
     */
    public List<TCodStation> getStation(SStationTypeDict sStationTypeDict){
        LambdaQueryWrapper<TCodStation> tCodStationLambdaQueryWrapper = new QueryWrapper<TCodStation>().lambda()
                .eq(TCodStation::getStationTypeDictId, sStationTypeDict.getDataId());
        return codStationMapper.selectList(tCodStationLambdaQueryWrapper);
    }

    /**
     * 获取站点浓度数据
     */
    public List<TCodStationDataAir> getCodeStationDataAir(List<TCodStation> codStations){
        List<String> stationIds = codStations.stream().map(TCodStation::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(stationIds)){
            return new ArrayList<>();
        }
        LambdaQueryWrapper<TCodStationDataAir> tCodStationDataAirLambdaQueryWrapper = new QueryWrapper<TCodStationDataAir>().lambda()
                .eq(TCodStationDataAir::getStationId, stationIds)
                .ge(TCodStationDataAir::getMonitorTime,getStartTime())
                .le(TCodStationDataAir::getMonitorTime,getEndTime());
        return codStationDataAirMapper.selectList(tCodStationDataAirLambdaQueryWrapper);
    }

    /**
     * 获取站点类型
     */
    public List<SStationTypeDict> getStationType(String[] typeIds){
        if (typeIds!=null&&typeIds.length>0){
            List<Integer> collect = Arrays.stream(typeIds).map(Integer::valueOf).collect(Collectors.toList());
            LambdaQueryWrapper<SStationTypeDict> sStationTypeDictLambdaQueryWrapper = new QueryWrapper<SStationTypeDict>().lambda()
                    .isNotNull(SStationTypeDict::getChartPollutant)
                    .in(SStationTypeDict::getDataId,collect);
            return stationTypeDictMapper.selectList(sStationTypeDictLambdaQueryWrapper);
        }
        return new ArrayList<>();
    }

    /**
     * “站点名称（国控/省控）”“指标”“年月日时”浓度为"x"ug/m3,高出市区浓度均值30%以上，产生一次报警。
     * @return
     */
    public String getOneAlarm(String stationName , String index , String date , BigDecimal concentration){
        return new StringBuilder()
                .append(stationName).append(index).append(date)
                .append("浓度为:").append(concentration).append("ug/m3").append("高出市区浓度均值30%以上，产生一次报警")
                .toString();
    }

    /**
     * 站点名称（国控/省控）”“指标”“年月日时”浓度为"x"ug/m3,高出市区浓度均值30%以上，“年月日时”浓度为“x”ug/m3,高出市区浓度均值15%以上，产生二次报警
     */
    public String getTwoAlarm(String stationName , String index , String date , BigDecimal concentration){
        return new StringBuilder()
                .append(stationName).append(index).append(date)
                .append("浓度为:").append(concentration).append("ug/m3").append("高出市区浓度均值15%以上，产生二次报警")
                .toString();
    }

    /**
     * “站点名称（乡镇站）”“指标”“年月日时”浓度为"x"ug/m3,大于等于“标准值”。
     */
    public String getLimitAlarm(String stationName , String index , String date , BigDecimal concentration , BigDecimal standard){
        return new StringBuilder()
                .append(stationName).append(index).append(date)
                .append("浓度为:").append(concentration).append("ug/m3").append("大于等于“").append(standard)
                .toString();
    }

    public AmProblemFi getAmProblemFi(TCodStation tCodStation, String message, String paramName){
        return AmProblemFi.builder()
                .problemSources("1")
                .problemType("003007")
                .problemDetails(message)
                .stationId(tCodStation.getId())
                .stationName(tCodStation.getStationName())
                .paramName(paramName)
                .analyseTime(new Date())
                .analyseTimeEnd(new Date())
                .type(1)
                .pollutantSourceAi("1").build();

    }

    public Date getStartTime(){
        return DateUtil.getDateHourBegin(DateUtil.getDateHour(-1));
    }

    public Date getEndTime(){
        return DateUtil.getDateHourEnd(DateUtil.getDateHour(-1));
    }

    public static enum LimitValueEnum{
        /**
         * PM2.5≥75ug/m³、PM10≥150ug/m³、SO≥20ug/m³、N02≥60ug/m³、C0≥1.4mg/m³
         */
        pm25("pm25",new BigDecimal(75)),
        pm10("pm10",new BigDecimal(150)),
        so2("so2",new BigDecimal(20)),
        N02("no2",new BigDecimal(60)),
        CO("co",new BigDecimal(1.4)),
        ;

        private String paramName;

        private BigDecimal value;

        LimitValueEnum(String paramName, BigDecimal value) {
            this.paramName = paramName;
            this.value = value;
        }

        public String getParamName() {
            return paramName;
        }

        public void setParamName(String paramName) {
            this.paramName = paramName;
        }

        public BigDecimal getValue() {
            return value;
        }

        public void setValue(BigDecimal value) {
            this.value = value;
        }

        public static LimitValueEnum geLimitValueByParam(String paramName){

            for (LimitValueEnum value : LimitValueEnum.values()) {
                if (value.getParamName().equals(paramName)){
                    return value;
                }
            }
            return null;
        }

    }

}
