package com.yeejoin.equipmanage.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.equipmanage.common.dto.OrgUsrDto;
import com.yeejoin.equipmanage.common.enums.PressurePumpRelateEnum;
import com.yeejoin.equipmanage.common.vo.IotDataVO;
import com.yeejoin.equipmanage.common.vo.PressurePumpCountVo;
import com.yeejoin.equipmanage.fegin.IotFeign;
import com.yeejoin.equipmanage.mapper.OrgUsrMapper;
import com.yeejoin.equipmanage.service.IPressurePumpService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import org.typroject.tyboot.core.restful.utils.ResponseModel;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PressurePumpServiceImpl implements IPressurePumpService {

    @Autowired
    private IotFeign iotFeign;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private OrgUsrMapper orgUsrMapper;

    @Value("classpath:/json/nameKeyInfo.json")
    private Resource nameKeyInfo;

    @Value("${equipment.pressurepump.start}")
    private String pressurePumpStart;

    @Value("${equipment.pressurepump.pipepressure}")
    private String pressurePumpPipePressure;

    @Override
    @Async
    public void saveDataToRedis(List<IotDataVO> iotDatalist, String iotCode, String bizOrgCode) {
        // 获取公司顶级bizOrgCode
        bizOrgCode = getCompanyBizOrgCode(bizOrgCode);
        if (StringUtils.isNotBlank(bizOrgCode)) {
            String pressurePumpValue = PressurePumpRelateEnum.PRESSURE_PUMP.getValue();
            //  获取配置JSON信息集合
            List<Map> list = getNameKeyInfoList(pressurePumpValue);
            if (CollectionUtils.isNotEmpty(list) && StringUtils.isNotBlank(iotCode) && StringUtils.isNotBlank(bizOrgCode)) {
                Map map = list.get(0);
                String nameKey = map.get("nameKey").toString();
                int expire = Integer.parseInt(map.get("expire").toString());
                String nowString = DateUtils.getDateNowString();
                long timeMillis = System.currentTimeMillis();
                for (IotDataVO vo : iotDatalist) {
                    String key = vo.getKey();
                    if (nameKey.contains(vo.getKey())) {
                        vo.setCreatedTime(nowString);
                        redisUtils.set(String.join(":", pressurePumpValue, bizOrgCode, key, iotCode, String.valueOf(timeMillis)), JSON.toJSONString(vo), expire);
                    }
                }
            }
        }
    }

    @Override
    public String getCompanyBizOrgCode(String bizOrgCode) {
        Map<String, String> params = new HashMap<>();
        params.put("bizOrgType", PressurePumpRelateEnum.BIZ_ORG_TYPE_COMPANY.getValue());
        params.put("bizOrgCode", bizOrgCode);
        List<OrgUsrDto> orgUsrDtoList = getOrgUsrDtoInfo(params);
        if (CollectionUtils.isNotEmpty(orgUsrDtoList)) {
            bizOrgCode = orgUsrDtoList.get(orgUsrDtoList.size() - 1).getBizOrgCode();
        }
        return bizOrgCode;
    }

    @Override
    public List<IotDataVO> getDataToRedis(String infoCode, String nameKey, String iotCode, String bizOrgCode) {
        List<IotDataVO> list = new ArrayList<>();
        Set<String> keys = redisUtils.getKeys(String.join(":", infoCode, bizOrgCode, nameKey, StringUtils.isNotEmpty(iotCode) ? iotCode : ""));
        if (CollectionUtils.isNotEmpty(keys)) {
            keys.forEach(x -> list.add(JSON.parseObject(redisUtils.get(x).toString(), IotDataVO.class)));
            //  时间倒序排序
            list.sort((t1, t2) -> t2.getCreatedTime().compareTo(t1.getCreatedTime()));
        }
        return list;
    }

    @Override
    public List<IotDataVO> getDataToRedisByDateBetween(String infoCode, String nameKey, String iotCode, Date startDate, Date endDate, String bizOrgCode) {
        List<IotDataVO> list = new ArrayList<>();
        Set<String> keys = redisUtils.getKeys(String.join(":", infoCode, bizOrgCode, nameKey, StringUtils.isNotEmpty(iotCode) ? iotCode : ""));
        if (CollectionUtils.isNotEmpty(keys)) {
            keys.forEach(x -> {
                String[] split = x.split(":");
                long time = split.length > 0 ? Long.parseLong(split[split.length - 1]) : 0;
                try {
                    Date date = DateUtils.convertStrToDate(DateUtils.stampToDate(time, DateUtils.DATE_TIME_PATTERN), DateUtils.DATE_TIME_PATTERN);
                    if (DateUtils.dateCompare(date, startDate) >= 0 && DateUtils.dateCompare(endDate, date) >= 0) {
                        list.add(JSON.parseObject(redisUtils.get(x).toString(), IotDataVO.class));
                    }
                } catch (ParseException e) {
                    log.error("getDataToRedisByDateBetween-->字符串转日期失败：{}", e.getMessage());
                }
            });
            //  时间倒序排序
            list.sort((t1, t2) -> t2.getCreatedTime().compareTo(t1.getCreatedTime()));
        }
        return list;
    }

    public List<PressurePumpCountVo> getCountDataToRedisByDateBetween(String infoCode, String countRedisKey, String nameKey, String iotCode, Date startDate, Date endDate, String bizOrgCode) {
        List<PressurePumpCountVo> list = new ArrayList<>();
        Set<String> keys = redisUtils.getKeys(String.join(":", infoCode, bizOrgCode, countRedisKey, nameKey, StringUtils.isNotEmpty(iotCode) ? iotCode : ""));
        if (CollectionUtils.isNotEmpty(keys)) {
            keys.forEach(x -> {
                String[] split = x.split(":");
                String time = split.length > 0 ? (split[split.length - 1]) : "";
                try {
                    Date date = DateUtils.convertStrToDate(time, DateUtils.DATE_PATTERN);
                    //  结束日期不包含今天，获取3天前数据
                    if (DateUtils.dateCompare(date, startDate) >= 0 && DateUtils.dateCompare(endDate, date) > 0) {
                        list.add(JSON.parseObject(redisUtils.get(x).toString(), PressurePumpCountVo.class));
                    }
                } catch (ParseException e) {
                    log.error("getDataToRedisByDateBetween-->字符串转日期失败：{}", e.getMessage());
                }
            });
            //  时间升序排序
            list.sort(Comparator.comparing(PressurePumpCountVo::getTime));
        }
        return list;
    }

    @Override
    public List<Map> getNameKeyInfoList(String code) {
        String json = null;
        try {
            json = IOUtils.toString(nameKeyInfo.getInputStream(), java.lang.String.valueOf(StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("获取指标JSON配置信息失败：{}", e.getMessage());
        }
        List<Map> list = JSON.parseArray(json, Map.class);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().filter(x -> code.equals(x.get("code"))).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public long getAllPressurePumpStartStopInterval(List<IotDataVO> dataList, List<IotDataVO> dataListFilterTrue, List<IotDataVO> dataListFilterFalse, String nowStrLong) {
        String intervalTime1 = nowStrLong;
        String intervalTime2 = nowStrLong;
        if (!ObjectUtils.isEmpty(dataListFilterTrue) && dataListFilterTrue.size() > 1) {
            //获取启动列表中
            intervalTime1 = dataListFilterTrue.get(0).getCreatedTime();
            intervalTime2 = dataListFilterTrue.get(1).getCreatedTime();
        }
        //  结果向上取整
        double ceil = Math.ceil(Math.abs(DateUtils.getDurationSeconds(intervalTime1, intervalTime2, DateUtils.DATE_TIME_PATTERN)) * 1.0 / Double.parseDouble(PressurePumpRelateEnum.ONE_HOUR_MINUTE.getValue()));
        return new Double(ceil).longValue();
    }

    @Override
    public List<IotDataVO> getDataListFilter(List<IotDataVO> dataList, String value) {
        return dataList.stream().filter(x -> x.getValue() != null && StringUtils.isNotBlank(x.getCreatedTime()) && value.equalsIgnoreCase(x.getValue().toString())).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<IotDataVO>> getDataList(List<Map<String, Object>> pumpInfoList, String infoCode, String equipmentCode, String top, String nameKey, String bizOrgCode, String iotCode) {
        Map<String, List<IotDataVO>> map = new HashMap<>();
        //  获取redis稳压泵缓存数据，默认JSON配置最近4小时
        List<IotDataVO> dataList = getDataToRedis(infoCode, nameKey, iotCode, bizOrgCode);
        //  过滤指定值数据
        List<IotDataVO> dataListFilterTrue = getDataListFilter(dataList, PressurePumpRelateEnum.IOT_INDEX_VALUE_TRUE.getValue());
        List<IotDataVO> dataListFilterFalse = getDataListFilter(dataList, PressurePumpRelateEnum.IOT_INDEX_VALUE_FALSE.getValue());
        //获取iot的数据
        if (pressurePumpPipePressure.equalsIgnoreCase(nameKey)) {
            if (ObjectUtils.isEmpty(dataList) || dataList.size() < 2) {
                dataList = getIotData(pumpInfoList, top, nameKey);
            }
        } else {
            if (ObjectUtils.isEmpty(dataList) || ObjectUtils.isEmpty(dataListFilterTrue) || ObjectUtils.isEmpty(dataListFilterFalse) || dataListFilterTrue.size() < 2) {
                dataList = getIotData(pumpInfoList, top, nameKey);
                dataListFilterTrue = getDataListFilter(dataList, PressurePumpRelateEnum.IOT_INDEX_VALUE_TRUE.getValue());
                dataListFilterFalse = getDataListFilter(dataList, PressurePumpRelateEnum.IOT_INDEX_VALUE_FALSE.getValue());
            }
        }
        map.put("dataList", dataList);
        map.put("dataListFilterTrue", dataListFilterTrue);
        map.put("dataListFilterFalse", dataListFilterFalse);
        return map;
    }

    private List<IotDataVO> getIotData(List<Map<String, Object>> pumpInfoList, String top, String nameKey) {
        List<IotDataVO> dataList = new ArrayList<>();
        //  通过 equipmentCode 获取装备
        if (!ObjectUtils.isEmpty(pumpInfoList)) {
            String iotCode = pumpInfoList.get(0).get("iotCode").toString();
            if (iotCode.length() > 8) {
                String prefix = iotCode.substring(0, 8);
                List<Map<String, String>> iotDataList = new ArrayList<>();
                try {
                    iotDataList = getIotTopSingleField(top, prefix, null, null, nameKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //将iot的List<Map<String, String>>转化为List<IotDataVO>类型
                if (0 < iotDataList.size()) {
                    iotDataList.forEach(e -> {
                        try {
                            IotDataVO iotDataVO = (IotDataVO) mapToObject(e, IotDataVO.class, nameKey);
                            dataList.add(iotDataVO);
                        } catch (Exception el) {
                            throw new BadRequest("IOT数据类型转换失败");
                        }
                    });
                }
            }
        } else {
            throw new BadRequest("装备物联编码错误，请确认！");
        }
        return dataList;
    }

    /**
     * map 转化为对象
     *
     * @param map    需要转化的参数
     * @param aClass 要转化成的对象
     * @return 转化成功的对象
     * @throws IllegalAccessException 非法访问异常
     * @throws InstantiationException 实例化异常
     */
    @Override
    public Object mapToObject(Map<String, String> map, Class<?> aClass, String indexKey) throws IllegalAccessException, InstantiationException {
        if (map.isEmpty()) {
            return null;
        }
        Object o = aClass.newInstance();
        Field[] declaredFields = o.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            int modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
                continue;
            }
            // （此处如果不设置 无法获取对象的私有属性）
            field.setAccessible(true);
            if ("key".equals(field.getName())) {
                field.set(o, indexKey);
            } else if ("value".equals(field.getName())) {
                field.set(o, map.get(indexKey));
            } else {
                field.set(o, map.get(field.getName()));
            }
        }
        return o;
    }

    @Override
    public Map<String, List<PressurePumpCountVo>> getDateRangeCountList(List<Map<String, Object>> pumpInfoList, String startTime, String endTime, String infoCode, String countRedisKey, String equipmentCode, String nameKey, long countExpire, String bizOrgCode) {
        Map<String, List<PressurePumpCountVo>> dataMap = new HashMap<>();
        try {
            if (CollectionUtils.isNotEmpty(pumpInfoList)) {
                for (Map<String, Object> map : pumpInfoList) {
                    //  iot获取数据，返回并存储redis
                    Object iotCode = map.get("iotCode");
                    if (!ObjectUtils.isEmpty(iotCode)) {
                        String iotCodeStr = iotCode.toString();
                        Date startDate = DateUtils.convertStrToDate(startTime, DateUtils.DATE_PATTERN);
                        Date endDate = DateUtils.convertStrToDate(endTime, DateUtils.DATE_PATTERN);
                        List<PressurePumpCountVo> dataList = getCountDataToRedisByDateBetween(PressurePumpRelateEnum.PRESSURE_PUMP.getValue(), countRedisKey, nameKey, iotCodeStr, startDate, endDate, bizOrgCode);
                        Long days = DateUtils.getDurationDays(startTime, endTime, DateUtils.DATE_TIME_PATTERN);
                        if (CollectionUtils.isEmpty(dataList) || (CollectionUtils.isNotEmpty(dataList) && dataList.size() < days)) {
                            String prefix = ObjectUtils.isEmpty(iotCode) ? "" : iotCodeStr.substring(0, 8);
                            String suffix = ObjectUtils.isEmpty(iotCode) ? "" : iotCodeStr.substring(8);
                            dataList = getIotCountData(startTime, endTime, infoCode, countRedisKey, prefix, suffix, PressurePumpRelateEnum.IOT_INDEX_VALUE_TRUE.getValue(), nameKey, countExpire, bizOrgCode);
                        }
                        dataMap.put(iotCodeStr, dataList);
                    }
                }
            }
        } catch (ParseException e) {
            log.error("获取稳压泵范围启动统计失败：{}", e.getMessage());
        }
        return dataMap;
    }

    @Override
    public List<PressurePumpCountVo> getIotCountData(String startTime, String endTime, String infoCode, String countRedisKey, String prefix, String suffix, String key, String fieldKey, long expire, String bizOrgCode) {
        List<Map<String, String>> dataMapList = getIotCommonListData(startTime, endTime, prefix, suffix, key, fieldKey);
        if (CollectionUtils.isNotEmpty(dataMapList) && StringUtils.isNotBlank(key)) {
            Map<String, List<Map<String, String>>> dataMap = dataMapList.stream().filter(y -> y.containsKey(PressurePumpRelateEnum.CREATED_TIME.getValue()) && y.get(fieldKey) != null && key.equalsIgnoreCase(y.get(fieldKey))).collect(Collectors.groupingBy(e -> e.get(PressurePumpRelateEnum.CREATED_TIME.getValue()).substring(0, 10)));
            if (!dataMap.isEmpty()) {
                List<PressurePumpCountVo> dataList = new ArrayList<>();
                String iotCode = String.join("", StringUtils.isNotEmpty(prefix) ? prefix : "", StringUtils.isNotEmpty(suffix) ? suffix : "");
                for (String time : dataMap.keySet()) {
                    PressurePumpCountVo countVo = new PressurePumpCountVo();
                    countVo.setTime(time);
                    countVo.setValue(dataMap.get(time).size());
                    // 获取公司顶级bizOrgCode
                    bizOrgCode = getCompanyBizOrgCode(bizOrgCode);
                    //  获取的数据存储到redis
                    String topic = String.join(":", infoCode, bizOrgCode, countRedisKey, fieldKey, iotCode, time);
                    countVo.setIotCode(topic);
                    redisUtils.set(topic, JSON.toJSONString(countVo), expire);
                    dataList.add(countVo);
                }
                return dataList;
            }
        }
        return Collections.emptyList();
    }

    private List<IotDataVO> getDataListFilter(List<IotDataVO> dataList, Date beforeDate) {
        if (beforeDate != null) {
            return dataList.stream().filter(x -> DateUtils.dateCompare(DateUtils.longStr2Date(x.getCreatedTime()), beforeDate) >= 0).collect(Collectors.toList());
        }
        return dataList;
    }

    @Override
    public int getAllPressurePumpStartFrequency(double hour, List<IotDataVO> dataList, Date dateNow) {
        if (CollectionUtils.isNotEmpty(dataList)) {
            Date beforeDate = DateUtils.dateAddMinutes(dateNow, (int) (hour * Integer.parseInt(PressurePumpRelateEnum.ONE_HOUR_MINUTE.getValue()) * -1));
            List<IotDataVO> collect = dataList.stream().filter(x -> DateUtils.dateCompare(DateUtils.longStr2Date(x.getCreatedTime()), beforeDate) >= 0).collect(Collectors.toList());
            return collect.size();
        }
        return 0;
    }

    @Override
    public long getAllPressurePumpStartStopDuration(List<IotDataVO> dataList, List<IotDataVO> dataListFilterTrue, List<IotDataVO> dataListFilterFalse, String nowStrLong) {
        String durationTime = nowStrLong;
        if (CollectionUtils.isNotEmpty(dataList)) {
            IotDataVO iotDataVO = dataList.get(0);
            String value = iotDataVO.getValue().toString();
            String createdTime = iotDataVO.getCreatedTime();
            if (PressurePumpRelateEnum.IOT_INDEX_VALUE_TRUE.getValue().equalsIgnoreCase(value)) {
                durationTime = createdTime;
            } else {
                if (CollectionUtils.isNotEmpty(dataListFilterFalse) && CollectionUtils.isNotEmpty(dataListFilterTrue)) {
                    //  获取最新启动时间
                    durationTime = dataListFilterTrue.get(0).getCreatedTime();
                    //  获取大于启动信号，且最近停止信号的时间
                    String finalDurationTime = durationTime;
                    List<IotDataVO> timeList = dataListFilterFalse.stream().filter(x -> DateUtils.getDurationSeconds(finalDurationTime, x.getCreatedTime(), DateUtils.DATE_TIME_PATTERN) >= 0).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(timeList)) {
                        nowStrLong = timeList.get(timeList.size() - 1).getCreatedTime();
                    }
                }
            }
        }
        //  结果向上取整
        double ceil = Math.ceil(Math.abs(DateUtils.getDurationSeconds(durationTime, nowStrLong, DateUtils.DATE_TIME_PATTERN)) * 1.0 / Double.parseDouble(PressurePumpRelateEnum.ONE_HOUR_MINUTE.getValue()));
        return new Double(ceil).longValue();
    }

    @Override
    public double getAllPressurePumpPipePressureDiff(List<IotDataVO> dataList, List<IotDataVO> dataPipeList, String minutes) {
        if (CollectionUtils.isNotEmpty(dataList) && CollectionUtils.isNotEmpty(dataPipeList)) {
            IotDataVO iotDataVO = dataList.get(0);
            String value = iotDataVO.getValue().toString();
            if (PressurePumpRelateEnum.IOT_INDEX_VALUE_TRUE.getValue().equalsIgnoreCase(value)) {
                List<IotDataVO> falseDataList = getDataListFilter(dataList, PressurePumpRelateEnum.IOT_INDEX_VALUE_FALSE.getValue());
                if (CollectionUtils.isNotEmpty(falseDataList)) {
                    String createdTime = falseDataList.get(0).getCreatedTime();
                    try {
                        Date stop5BeforeDate = DateUtils.dateAddMinutes(DateUtils.convertStrToDate(createdTime, DateUtils.DATE_TIME_PATTERN), Integer.parseInt(minutes));
                        //  获取指定之前时间，指定值数据
                        List<IotDataVO> dataFilterList = getDataListFilter(dataPipeList, stop5BeforeDate);
                        if (CollectionUtils.isNotEmpty(dataFilterList)) {
                            Object value1 = dataPipeList.get(0).getValue();
                            Object value2 = dataPipeList.get(dataFilterList.size() - 1).getValue();
                            if (!ObjectUtils.isEmpty(value1) && !ObjectUtils.isEmpty(value2)) {
                                double val1 = Double.parseDouble(value1.toString());
                                double val2 = Double.parseDouble(value2.toString());
                                return Math.abs(val1 - val2);
                            }
                        }
                    } catch (ParseException e) {
                        log.error("获取所有稳压泵管网压力差失败：{}", e.getMessage());
                    }
                }
            }
        }
        return 0;
    }

    @Override
    public List<Map<String, String>> getIotTopSingleField(String top, String productKey, String deviceName, String key, String fieldKey) {
        ResponseModel responseModel = null;
        try {
            responseModel = iotFeign.topSingleField(top, productKey, deviceName, key, fieldKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (responseModel != null && 200 == responseModel.getStatus()) {
            String json = JSON.toJSONString(responseModel.getResult());
            return (List<Map<String, String>>) JSONArray.parse(json);
        }
        return Collections.emptyList();
    }

    @Override
    public List<Map<String, String>> getIotCommonListData(String startTime, String endTime, String prefix, String suffix, String key, String fieldKey) {
        ResponseModel responseModel = null;
        try {
            responseModel = iotFeign.selectListNew(prefix, suffix, startTime, endTime, key, fieldKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (responseModel != null && 200 == responseModel.getStatus()) {
            String json = JSON.toJSONString(responseModel.getResult());
            return (List<Map<String, String>>) JSONArray.parse(json);
        }
        return Collections.emptyList();
    }

    public List<OrgUsrDto> getOrgUsrDtoInfo(Map<String, String> map) {
        return orgUsrMapper.getOrgUsrDtoInfo(map);
    }

}
