package com.zksy.environmentHJ212.utils;

import com.zksy.environmentHJ212.domain.po.MinuteDataInfo;
import com.zksy.environmentHJ212.domain.po.PsMonitorSz;
import com.zksy.environmentHJ212.service.DayDataInfoService;
import com.zksy.environmentHJ212.service.HourDataInfoService;
import com.zksy.environmentHJ212.service.MinuteDataInfoService;
import com.zksy.environmentHJ212.service.PsMonitorSzService;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;

/**
 * @Description
 * @Date 2021- 06-01-下午 14:37
 * @auther Javen
 */
@Service
@Slf4j
@Scope(SCOPE_PROTOTYPE)
public class MonitorDatasynch {
    private final PsMonitorSzService psMonitorSzService;
    private final MinuteDataInfoService minuteDataInfoService;
    private final HourDataInfoService hourDataInfoService;
    private final DayDataInfoService  dayDataInfoService;

    @Autowired
    public MonitorDatasynch(PsMonitorSzService psMonitorSzService, MinuteDataInfoService minuteDataInfoService, HourDataInfoService hourDataInfoService, DayDataInfoService dayDataInfoService) {
        this.psMonitorSzService = psMonitorSzService;
        this.minuteDataInfoService = minuteDataInfoService;
        this.hourDataInfoService = hourDataInfoService;
        this.dayDataInfoService = dayDataInfoService;
    }
    private static LinkedHashMap<String, String> fieldMap = new LinkedHashMap<>();
    static {
        fieldMap.put("a01001", "TEMP");// 温度
        fieldMap.put("a01002", "HUMIDITY");// 湿度
        fieldMap.put("DataTime", "SAMPLETIME");// 采样时间
        fieldMap.put("CN", "CN");// 命令编码
        fieldMap.put("ST", "ST");// 系统编码
        fieldMap.put("MN", "MN");// 设备唯一标志
    }
    /**
     * 获取水质同步数据，数据有效性校验，解析
     */
    public String pushMonitorData(ChannelHandlerContext channelHandlerContext, String oldDataStr){
        log.info("==========接收到数据：" +  oldDataStr + "==========");
        MinuteDataInfo monitordata = null;
        String response = null;
        if (oldDataStr.length() > 6) {
            String lenstr = oldDataStr.substring(2, 6);
            // 获取数据长度
            int codeLen = Integer.parseInt(lenstr);
            log.info("数据主体长度：" + codeLen);
            // 获取数据正文
            String dataStr = oldDataStr.substring(6, oldDataStr.length()-6);
            log.info("数据主体正文：" + dataStr);
            // 获取数据CRC16检验码
            String code = oldDataStr.substring(oldDataStr.length()-6, oldDataStr.length()-2);
            log.info(code);
            // 通过数据生成CRC16校验码
            String nowCode = DataCheckUtil.crc16_Checkout(dataStr, codeLen);
            log.info("==========自带的验证码：" + code + "新生成的验证码：" + nowCode);
            // 前后crc16校验码比对（true-数据传输无问题，false-数据传输出现问题）
            if (code.equals(nowCode)) {
                try {
                    String[] dataArr = dataStr.split(";");
                    List<String> collect = Arrays.asList(dataArr).stream().filter(item -> item.contains("CN=")).collect(Collectors.toList());
                    String cn = "";
                    if (collect != null && collect.size() > 0) {
                        cn = collect.get(0);
                    }
                    // 判断是实时数据（2011）
                    if ("2011".equals(cn.substring(3))) {
                        String host = ((InetSocketAddress)channelHandlerContext.channel().remoteAddress()).getAddress().getHostAddress();
                        int port = ((InetSocketAddress)channelHandlerContext.channel().remoteAddress()).getPort();
                        String ipPort = host+":"+port;
                        List<PsMonitorSz> psMonitorSzList = psMonitorSzService.pushMonitorData(oldDataStr,dataArr,ipPort);
                        psMonitorSzService.saveBatch(psMonitorSzList);
                    } else if ("2051".equals(cn.substring(3))){
                        //分钟数据
                        minuteDataInfoService.saveMonitordata(oldDataStr,dataArr);
                    }else if ("2061".equals(cn.substring(3))){
                        //小时数据
                        hourDataInfoService.saveHourData(oldDataStr,dataArr);
                    }else if ("2031".equals(cn.substring(3))){
                        log.info("日数据========", oldDataStr);
                        //日数据
                        dayDataInfoService.saveDayData(oldDataStr,dataArr);
                    }else {
                        log.info("其它数据========", oldDataStr);
                    }

                } catch (Exception e) {
                    log.info("==========数据解析错误==========", oldDataStr);
                    e.printStackTrace();
                }
            } else {
                log.info("==========数据传输错误，验证不匹配==========", oldDataStr);
            }
        } else {
            log.info("==========数据长度格式不正确==========");
        }
        return response;
    }
    /*public static List<MinuteDataInfo> minuteDataInfoData(String oldDataStr,String[] dataArr) {
        ArrayList<String> arrayList = new ArrayList<>();
        List<MinuteDataInfo> minuteDataInfolist = new ArrayList<>();
        for (String param : dataArr) {
            System.out.println("param = " + param);
            String String = "&&";
            boolean contains = param.contains(String);
            if (contains) {
                param = param.replace(String, ",");

            }
            if (param.contains(",")) {
                arrayList.add(param);
            }

        }
        ArrayList<String> objects = new ArrayList<>();
        for (String entity : arrayList) {
            String substring = null;
            if (entity.contains("-")) {
                substring = entity.substring(0, entity.indexOf("-"));
            }
            if (StringUtils.isBlank(substring)) {
                continue;
            }
            if (entity.startsWith(substring)) {
                objects.add(substring);
                String[] split = entity.split(",");

                MinuteDataInfo minuteDataInfo = new MinuteDataInfo();
                minuteDataInfo.setRemark(oldDataStr);
                for (String parameters : split) {

                    String[] split1 = parameters.split("=");
                    minuteDataInfo.setCode(substring);
                    if (parameters.startsWith(substring + "-Cou=")) {

                        minuteDataInfo.setDataCouValue(new BigDecimal(split1[1]));
                    }
                    if (parameters.startsWith(substring + "-Min=")) {
                        minuteDataInfo.setDataMinValue(new BigDecimal(split1[1]));
                    }
                    if (parameters.startsWith(substring + "-Avg=")) {
                        minuteDataInfo.setDataAvgValue(new BigDecimal(split1[1]));
                    }
                    if (parameters.startsWith(substring + "-Max=")) {
                        minuteDataInfo.setDataMaxValue(new BigDecimal(split1[1]));
                    }
                    if (parameters.startsWith(substring + "-Flag=")) {
                        minuteDataInfo.setDataFlag(split1[1]);
                    }
                    minuteDataInfo.setCreateTime(LocalDateTime.now());
                    minuteDataInfo.setId(UUID.randomUUID().toString().replace("-", ""));
                    for (String comParams : dataArr) {
                        if (comParams.contains("&&") && !comParams.contains(",")) {
                            String replace = comParams.replace("&&", ",");
                            String[] split2 = replace.split(",");
                            if (split2.length>1) {
                                String dateString = split2[1];
                                if (dateString.startsWith("DataTime=")) {
                                    minuteDataInfo.setDataTime(DateTimeUtil.parseDateTime(dateString.split("=")[1]));
                                }
                            }
                        }
                        String[] comsplit1 = comParams.split("=");
                        String paraname = comsplit1[0];
                        String value = comsplit1[1];
                        if (paraname.equals("MN")) {
                            minuteDataInfo.setMn(value);
                        }
                        if (paraname.equals("Flag")) {
                            minuteDataInfo.setFlag(value);
                        }
                    }
                }
                minuteDataInfolist.add(minuteDataInfo);
            }
        }
        return minuteDataInfolist;
    }*/
        public static <T> List<T> parseData(String oldDataStr, String[] dataArr, Supplier<T> entitySupplier,
                                            Consumer<EntityFieldMapperBuilder<T>> configurer) {
            EntityFieldMapperBuilder<T> builder = new EntityFieldMapperBuilder<>(entitySupplier);
            configurer.accept(builder);
            EntityFieldMapper<T> mapper = builder.build();

            List<String> validParams = extractValidParams(dataArr);
            Map<String, String> commonParams = extractCommonParams(dataArr);

            List<T> entityList = new ArrayList<>();
            for (String entityParam : validParams) {
                String code = extractCode(entityParam);
                if (code == null) {
                    continue;
                }

                T entity = mapper.createEntity();
                mapper.setCode(entity, code);
                mapper.setRemark(entity, oldDataStr);

                String[] parameters = entityParam.split(",");
                for (String param : parameters) {
                    String[] keyValue = param.split("=", 2);
                    if (keyValue.length != 2) {
                        continue;
                    }
                    mapper.mapField(entity, code, keyValue[0], keyValue[1]);
                }

                mapper.setCommonFields(entity, commonParams);
                mapper.setCreateTime(entity);
                mapper.setId(entity);

                entityList.add(entity);
            }
            return entityList;
        }

        private static List<String> extractValidParams(String[] dataArr) {
            List<String> validParams = new ArrayList<>();
            for (String param : dataArr) {
                if (param.contains("&&")) {
                    param = param.replace("&&", ",");
                }
                if (param.contains(",")) {
                    validParams.add(param);
                }
            }
            return validParams;
        }

        private static Map<String, String> extractCommonParams(String[] dataArr) {
            Map<String, String> commonParams = new HashMap<>();
            for (String param : dataArr) {
                if (param.contains("&&") && !param.contains(",")) {
                    String[] parts = param.replace("&&", ",").split(",");
                    if (parts.length > 1 && parts[1].startsWith("DataTime=")) {
                        commonParams.put("DataTime", parts[1].substring("DataTime=".length()));
                    }
                }
                String[] keyValue = param.split("=", 2);
                if (keyValue.length == 2) {
                    commonParams.put(keyValue[0], keyValue[1]);
                }
            }
            return commonParams;
        }

        private static String extractCode(String entityParam) {
            if (entityParam.contains("-")) {
                return entityParam.substring(0, entityParam.indexOf("-"));
            }
            return null;
        }

        public static class EntityFieldMapperBuilder<T> {
            private final Supplier<T> entitySupplier;
            private final Map<String, BiConsumer<T, Object>> fieldMappers = new HashMap<>();
            private BiConsumer<T, String> codeMapper;
            private BiConsumer<T, String> remarkMapper;
            private BiConsumer<T, String> mnMapper;
            private BiConsumer<T, String> flagMapper;
            private BiConsumer<T, String> dataTimeMapper;
            private BiConsumer<T, LocalDateTime> createTimeMapper;
            private BiConsumer<T, String> idMapper;

            public EntityFieldMapperBuilder(Supplier<T> entitySupplier) {
                this.entitySupplier = entitySupplier;
            }

            public <V> EntityFieldMapperBuilder<T> addFieldMapper(String suffix, BiConsumer<T, V> setter, Function<String, V> converter) {
                fieldMappers.put(suffix, (entity, value) -> setter.accept(entity, converter.apply((String) value)));
                return this;
            }

            public EntityFieldMapperBuilder<T> setCodeMapper(BiConsumer<T, String> codeMapper) {
                this.codeMapper = codeMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setRemarkMapper(BiConsumer<T, String> remarkMapper) {
                this.remarkMapper = remarkMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setMnMapper(BiConsumer<T, String> mnMapper) {
                this.mnMapper = mnMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setFlagMapper(BiConsumer<T, String> flagMapper) {
                this.flagMapper = flagMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setDataTimeMapper(BiConsumer<T, String> dataTimeMapper) {
                this.dataTimeMapper = dataTimeMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setCreateTimeMapper(BiConsumer<T, LocalDateTime> createTimeMapper) {
                this.createTimeMapper = createTimeMapper;
                return this;
            }

            public EntityFieldMapperBuilder<T> setIdMapper(BiConsumer<T, String> idMapper) {
                this.idMapper = idMapper;
                return this;
            }

            public EntityFieldMapper<T> build() {
                return new EntityFieldMapper<>(
                        entitySupplier, fieldMappers, codeMapper, remarkMapper,
                        mnMapper, flagMapper, dataTimeMapper, createTimeMapper, idMapper
                );
            }
        }

        public static class EntityFieldMapper<T> {
            private final Supplier<T> entitySupplier;
            private final Map<String, BiConsumer<T, Object>> fieldMappers;
            private final BiConsumer<T, String> codeMapper;
            private final BiConsumer<T, String> remarkMapper;
            private final BiConsumer<T, String> mnMapper;
            private final BiConsumer<T, String> flagMapper;
            private final BiConsumer<T, String> dataTimeMapper;
            private final BiConsumer<T, LocalDateTime> createTimeMapper;
            private final BiConsumer<T, String> idMapper;

            public EntityFieldMapper(
                    Supplier<T> entitySupplier, Map<String, BiConsumer<T, Object>> fieldMappers,
                    BiConsumer<T, String> codeMapper, BiConsumer<T, String> remarkMapper,
                    BiConsumer<T, String> mnMapper, BiConsumer<T, String> flagMapper,
                    BiConsumer<T, String> dataTimeMapper, BiConsumer<T, LocalDateTime> createTimeMapper,
                    BiConsumer<T, String> idMapper) {
                this.entitySupplier = entitySupplier;
                this.fieldMappers = fieldMappers;
                this.codeMapper = codeMapper;
                this.remarkMapper = remarkMapper;
                this.mnMapper = mnMapper;
                this.flagMapper = flagMapper;
                this.dataTimeMapper = dataTimeMapper;
                this.createTimeMapper = createTimeMapper;
                this.idMapper = idMapper;
            }

            public T createEntity() {
                return entitySupplier.get();
            }

            public void setCode(T entity, String code) {
                if (codeMapper != null) {
                    codeMapper.accept(entity, code);
                }
            }

            public void setRemark(T entity, String remark) {
                if (remarkMapper != null) {
                    remarkMapper.accept(entity, remark);
                }
            }

            public void mapField(T entity, String code, String key, String value) {
                for (Map.Entry<String, BiConsumer<T, Object>> entry : fieldMappers.entrySet()) {
                    String suffix = entry.getKey();
                    BiConsumer<T, Object> mapper = entry.getValue();
                    if (key.equals(code + "-" + suffix)) {
                        mapper.accept(entity, value);
                        break;
                    }
                }
            }

            public void setCommonFields(T entity, Map<String, String> commonParams) {
                if (mnMapper != null && commonParams.containsKey("MN")) {
                    mnMapper.accept(entity, commonParams.get("MN"));
                }
                if (flagMapper != null && commonParams.containsKey("Flag")) {
                    flagMapper.accept(entity, commonParams.get("Flag"));
                }
                if (dataTimeMapper != null && commonParams.containsKey("DataTime")) {
                    dataTimeMapper.accept(entity, commonParams.get("DataTime"));
                }
            }

            public void setCreateTime(T entity) {
                if (createTimeMapper != null) {
                    createTimeMapper.accept(entity, LocalDateTime.now());
                }
            }

            public void setId(T entity) {
                if (idMapper != null) {
                    idMapper.accept(entity, UUID.randomUUID().toString().replace("-", ""));
                }
            }
        }
}
