package com.jjt.utils;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.jjt.biz.domain.TwinDevice;
import com.jjt.calc.domain.TwinCalcHour;
import com.jjt.calc.domain.TwinCalcHourSpec;
import com.jjt.calc.domain.TwinRecordAlarms;
import com.jjt.calc.domain.TwinRecordStop;
import com.jjt.common.utils.DateUtils;
import com.jjt.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 多线程执行任务
 *
 * @author wukai
 * @date 2024/5/4 20:35
 */
@Service
@Slf4j
public class AsyncService {
    @Resource
    private IotService iotService;
    /**
     * 小经编字段列表，方便看下标
     */
    private final String[] xFields = {
            "Capacity_data_1", "Capacity_data_2", "Capacity_data_15", "Capacity_data_16", "Capacity_data_17",
            "Capacity_data_18", "Capacity_data_19", "Capacity_data_48",
            "Formula_data_3", "Formula_data_15", "Formula_data_24", "Alarm_unit_1",
            "Alarm_unit_10", "Alarm_unit_11", "Alarm_unit_12", "Alarm_unit_13", "Alarm_unit_14",
            "Alarm_unit_15", "Alarm_unit_16", "Alarm_unit_17", "Alarm_unit_18", "Alarm_unit_19",
            "Alarm_unit_2", "Alarm_unit_20", "Alarm_unit_21", "Alarm_unit_22", "Alarm_unit_23",
            "Alarm_unit_24", "Alarm_unit_25", "Alarm_unit_26", "Alarm_unit_27", "Alarm_unit_3",
            "Alarm_unit_4", "Alarm_unit_5", "Alarm_unit_6", "Alarm_unit_7", "Alarm_unit_8",
            "Alarm_unit_9"
    };

    /**
     * 大经编字段列表，方便看下标
     */
    private final String[] dFields = {
            "Capacity_data_117 AS Capacity_data_1", "Capacity_data_177 AS Capacity_data_2", "Capacity_data_15", "Capacity_data_16", "Capacity_data_17",
            "Capacity_data_18", "Capacity_data_19", "Capacity_data_118 AS Capacity_data_48",
            "Formula_data_43 AS Formula_data_3", "Formula_data_55 AS Formula_data_15", "Capacity_data_112 AS Formula_data_24"
    };


    /**
     * 字段列表，方便查找位置
     */
    private final List<String> fieldList = Arrays.stream(xFields).collect(Collectors.toList());

    @Async("threadPoolTaskExecutor")
    public Future<Map<String, Object>> currData(TwinDevice twinDevice) {
        String table = twinDevice.getDevicePath();
        String sql = "select last * from " + table;
        JSONObject jsonObject = iotService.query(sql);
        JSONObject data = jsonObject.getJSONObject("data");
        JSONArray values = data.getJSONArray("values");
        JSONArray timestamps = data.getJSONArray("timestamps");
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("device", twinDevice);
        if (timestamps.size() > 0 && (System.currentTimeMillis() - new Date(timestamps.getLong(0)).getTime()) > 5 * 60 * 100) {
            //如果最后一条记录 超过5分钟，则视为无效记录
            dataMap.put("total", 0);
        } else {
            dataMap.put("total", values.size());
            dataMap.putAll(Tools.json2Map(values, table));
        }
        return new AsyncResult<>(dataMap);
    }

    /**
     * 根据设备获取指定时段数据
     *
     * @param twinDevice
     * @param start      开始时间
     * @param end        结束时间
     * @return
     */
    @Async("threadPoolTaskExecutor")
    public Future<Map<String, List<?>>> process(TwinDevice twinDevice, LocalDateTime start, LocalDateTime end) {
        //开始时间都减了一秒的，这里要加回来,用于计算时段等数据
        LocalDateTime ldt = start.plusSeconds(1);
        Date date = DateUtils.toDate(ldt.toLocalDate());
        int period = ldt.getHour();
        Map<String, List<?>> result = new HashMap<>(16);
        List<TwinCalcHour> calcHours = new ArrayList<>();
        TwinCalcHour hour = new TwinCalcHour();
        hour.setDeviceId(twinDevice.getDeviceId());
        hour.setDataDate(date);
        hour.setHour(period);
        Map<String, Object> map = calc(twinDevice, start, end);

        //0.已织造米数  1.总能耗 2.总重量 3.开机时间 4.关机时间
        float[] total = (float[]) map.get("total");
        hour.setLength(BigDecimal.valueOf(total[0]));
        hour.setWeight(BigDecimal.valueOf(total[2]));
        hour.setKwh(BigDecimal.valueOf(total[1]));
        hour.setOpenTime((long) total[3]);
        hour.setCloseTime((long) total[4]);
        calcHours.add(hour);

        List<TwinCalcHourSpec> specs = (List<TwinCalcHourSpec>) map.get("specList");
        specs.forEach(spec -> {
            spec.setDeviceId(twinDevice.getDeviceId());
            spec.setDataDate(date);
            spec.setHour(period);
        });

        List<TwinRecordStop> stopRecord = (List<TwinRecordStop>) map.get("stopRecord");
        List<TwinRecordStop> newStopRecord = new ArrayList<>();
        Map<Integer, TwinRecordStop> stopMap = new HashMap<>();
        stopRecord.forEach(stop -> {
            if (stop.getStartTime() != null) {
                stop.setDeviceId(twinDevice.getDeviceId());
                stop.setDataDate(date);
                stop.setHour(period);
                //将开始时间不为空的放入map
                stopMap.put(stop.getStopType(), stop);
            }
            if (stop.getEndTime() != null) {
                //将结束时间不为空组合，然后删除map中的对象，加入到新的List
                TwinRecordStop rs = stopMap.get(stop.getStopType());
                if (rs != null) {
                    rs.setEndTime(stop.getEndTime());
                    rs.setCalcStatus("0");
                    newStopRecord.add(rs);
                }
                stopMap.remove(stop.getStopType());
            }
        });

        //最后再补一次 没有结束时间的为当前时间
        for (TwinRecordStop stop : stopMap.values()) {
            stop.setCalcStatus("1");
            stop.setEndTime(DateUtils.toDate(end));
            newStopRecord.add(stop);
        }

        List<TwinRecordAlarms> alarmRecord = (List<TwinRecordAlarms>) map.get("alarmRecord");
        List<TwinRecordAlarms> newAlarmRecord = new ArrayList<>();
        Map<Integer, TwinRecordAlarms> alarmMap = new HashMap<>();
        alarmRecord.forEach(alarms -> {
            if (alarms.getStartTime() != null) {
                alarms.setDeviceId(twinDevice.getDeviceId());
                alarms.setDataDate(date);
                alarms.setHour(period);
                //将开始时间不为空的放入map
                alarmMap.put(alarms.getAlarmType(), alarms);
            }
            if (alarms.getEndTime() != null) {
                //将结束时间不为空组合，然后删除map中的对象，加入到新的List
                TwinRecordAlarms rs = alarmMap.get(alarms.getAlarmType());
                if (rs != null) {
                    rs.setEndTime(alarms.getEndTime());
                    rs.setCalcStatus("0");
                    newAlarmRecord.add(rs);
                }
                alarmMap.remove(alarms.getAlarmType());
            }
        });

        //最后再补一次 没有结束时间的为当前时间
        for (TwinRecordAlarms alarms : alarmMap.values()) {
            alarms.setCalcStatus("1");
            alarms.setEndTime(DateUtils.toDate(end));
            newAlarmRecord.add(alarms);
        }

        result.put("calc", calcHours);
        result.put("stopRecord", newStopRecord);
        result.put("alarmRecord", newAlarmRecord);
        result.put("specList", specs);
        return new AsyncResult<>(result);
    }


    /**
     * //    0        Capacity_data_2	已织造米数
     * //    7        Capacity_data_48	停机状态
     * //    8        Formula_data_3	米克重
     * //    9        Formula_data_13	卷曲幅宽
     */
    public Map<String, Object> calc(TwinDevice twinDevice, LocalDateTime start, LocalDateTime end) {
        //为true表示小经编，false表示大经编
        boolean flag = twinDevice.getDeviceCode().startsWith("C_");
        String table = twinDevice.getDevicePath();
        String[] fields = xFields;
        if (!flag) {
            fields = dFields;
        }
        String sql = "select %s from %s where time>%s and time <=%s";
        sql = String.format(sql, String.join(",", fields), table, DateUtils.parseIso(start), DateUtils.parseIso(end));
        long s = System.currentTimeMillis();
        JSONObject jsonObject = iotService.query(sql);
        long e = System.currentTimeMillis();
        log.info("接口耗时:{}ms,table:{},time:{}", e - s, table, end);
        JSONObject data = jsonObject.getJSONObject("data");
        JSONArray values = data.getJSONArray("values");
        values = mergedValues(values);

        JSONArray timestamps = data.getJSONArray("timestamps");
        //初始时间点数据
        //0-data2,1-data37,2-data38,3-data39,4-data42,5-data-43,6data-44
        //2024-06-26 1-6不要了 0织造米长 1.电量
        List<TwinCalcHourSpec> specList = new ArrayList<>();
        //1.米长 2.电量  3.密度
        float[] first = new float[3];
        //上一个时间点数据
        float[] last = new float[3];
        //统计数据,后面2个分别代表0.织造米长,1.电量 2.重量,3.开机时间,4.停机时间
        float[] total = new float[5];
        //米克重
        int lastMkz = 0;
        //毛高
        float lastHeight = 0f;
        //上次密度记录的米长
        float lastSpecLength = 0f;
        int last48 = 0;
        //告警时间记录
        List<TwinRecordAlarms> alarmRecord = new ArrayList<>();
        //停机记录
        List<TwinRecordStop> stopRecord = new ArrayList<>();

        for (int i = 0; i < values.size(); i++) {
            JSONArray da = values.getJSONArray(i);
            //当前时间数据 米长，正向电能，牵拉密度 2025-06-19 取消电量计算没必要了
            float[] curr = {da.getFloat(fieldList.indexOf("Capacity_data_2")), 0f, da.getFloat(fieldList.indexOf("Formula_data_24"))};
            int curr48 = da.getInt(fieldList.indexOf("Capacity_data_48"));

            if (i == 0) {
                //第一次数据是上次最后一条，只做记录用，不做处理
                first = curr.clone();
                last = curr.clone();
                if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_3")))) {
                    lastMkz = da.getInt(fieldList.indexOf("Formula_data_3"));
                }
                if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_15")))) {
                    lastHeight = da.getFloat(fieldList.indexOf("Formula_data_15"));
                }
                last48 = curr48;
                continue;
            }

            if (last[2] != curr[2]) {
                calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, specList);
                lastSpecLength = total[0];
            }
            //如果当前值为小于上一个，且上一个值不为0，则计算
            //因为会出现数据波动，停机再启动之后的第一个点不为0，为0.00几几几的
            //计算累加类的数据
            //这里只计算米长
            if (curr[0] < last[0] && last[0] != 0f) {
//                calcTotal(0, last, first, total, lastMkz, lastFk);
                calcTotal(0, last, first, total, lastMkz);
                first[0] = curr[0];
            }
            if (flag) {
                calcAlarms(values, timestamps.getLong(i), alarmRecord, i);
            }
            calcStops(curr48, last48, timestamps.getLong(i), total, stopRecord, i);

            //复制数组,设置last值为当前值
            last = curr.clone();
            if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_3")))) {
                if (da.getInt(fieldList.indexOf("Formula_data_3")) != 0) {
                    lastMkz = da.getInt(fieldList.indexOf("Formula_data_3"));
                }
            }
            if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_15")))) {
                lastHeight = da.getFloat(fieldList.indexOf("Formula_data_15"));
            }
            last48 = curr48;
        }
        //还是只计算米长
        calcTotal(0, last, first, total, lastMkz);

        calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, specList);
        total[3] = 3600 - total[4];

        Map<String, Object> result = new HashMap<>(16);
        result.put("total", total);
        result.put("stopRecord", stopRecord);
        result.put("alarmRecord", alarmRecord);
        result.put("specList", specList);
        return result;
    }

    /**
     * 处理数据 一会这儿有，一会那个有的情况
     *
     * @param values 原始数据
     */
    private JSONArray mergedValues(JSONArray values) {
        /*
         * 合并values数组中的数据，将缺失的数据补全
         * 1. 遍历values数组，从第二条记录开始(第一条作为基准)
         * 2. 将后续记录中非空的字段值补到第一条记录的对应位置
         * 3. 当第一条记录所有字段都非空时停止处理
         */
        JSONArray mergedValues = new JSONArray();
        JSONArray firstValues = values.getJSONArray(0);
        for (int i = 1; i < values.size(); i++) {
            JSONArray curr = values.getJSONArray(i);
            // 遍历当前记录的每个字段
            for (int j = 0; j < curr.size(); j++) {
                // 如果当前字段有值且第一条记录的对应字段为空，则补充值
                if (curr.get(j) != null && !curr.get(j).toString().isEmpty()
                        && (firstValues.get(j) == null || firstValues.get(j).toString().isEmpty())) {
                    firstValues.set(j, curr.get(j));
                }
            }

            // 检查第一条记录是否所有字段都已补全
            boolean isAll = true;
            for (Object firstValue : firstValues) {
                if (firstValue == null || firstValue.toString().isEmpty()) {
                    isAll = false;
                    break;
                }
            }

            // 如果所有字段都已补全，则停止处理
            if (isAll) {
                break;
            }
        }


        for (int i = 0; i < values.size(); i++) {
            JSONArray curr = values.getJSONArray(i);
            JSONArray merged = new JSONArray();
            if (i > 0) {
                // 取前一条记录作为基准
                JSONArray prev = mergedValues.getJSONArray(i - 1);
                for (int j = 0; j < curr.size(); j++) {
                    if (curr.get(j) != null && !curr.get(j).toString().isEmpty()) {
                        merged.add(curr.get(j));
                    } else {
                        merged.add(prev.get(j));
                    }
                }
            } else {
                // 第一条记录直接使用
                merged = curr;
            }
            mergedValues.add(merged);
        }
        return mergedValues;
    }

    /**
     * 按规格计算米长
     *
     * @param len         当前米长
     * @param lastLen     上次记录时米长
     * @param lastDensity 密度
     * @param lastMkz     米克重
     * @param specList    列表
     */
    private void calcSpec(float len, float lastLen, float lastDensity, int lastMkz, float lastHeight, List<TwinCalcHourSpec> specList) {
        //计算规格米长
        //如果密度有变化，就记录下来
        TwinCalcHourSpec spec = new TwinCalcHourSpec();
        spec.setDensity(BigDecimal.valueOf(lastDensity));
        float len1 = len - lastLen;
        spec.setLength(BigDecimal.valueOf(len1));
        spec.setMick(lastMkz);
        spec.setHeight(BigDecimal.valueOf(lastHeight));
        specList.add(spec);
    }


    /**
     * 停机次数计算
     * 停机处理
     *
     * @param curr       当前值
     * @param last       上一条记录的值
     * @param time       时间
     * @param total      统计
     * @param stopRecord 记录
     * @param i          当前时序
     */
    private void calcStops(int curr, int last, long time, float[] total, List<TwinRecordStop> stopRecord, int i) {
        //取消毫秒
        time = time / 1000 * 1000;
        TwinRecordStop stop = new TwinRecordStop();
        //如果第一条记录不为0，则直接记录停机开始时间
        if (i == 1 && curr != 0) {
            stop = new TwinRecordStop();
            //记录停机开始时间
            stop.setStopType(curr);
            stop.setStartTime(new Date(time));
            stopRecord.add(stop);
            return;
        }
        if (curr != 0) {
            total[4]++;
            if (curr != last) {
                if (last != 0) {
                    //记录上个停机类型的结束时间
                    stop.setStopType(last);
                    stop.setEndTime(new Date(time));
                    stopRecord.add(stop);
                }
                stop = new TwinRecordStop();
                //记录停机开始时间
                stop.setStopType(curr);
                stop.setStartTime(new Date(time));
                stopRecord.add(stop);
            }
        } else if (last != 0) {
            //判断停机结束之后的开机
            stop.setStopType(last);
            stop.setEndTime(new Date(time));
            stopRecord.add(stop);
        }
    }

    /**
     * 告警计算
     *
     * @param values      所有记录
     * @param time        当前时间
     * @param alarmRecord 告警记录
     * @param i           时序
     */
    private void calcAlarms(JSONArray values, long time, List<TwinRecordAlarms> alarmRecord, int i) {
        JSONArray curr = values.getJSONArray(i);
        JSONArray last = values.getJSONArray(i - 1);
        TwinRecordAlarms recordAlarms;
        int index = 26;
        boolean[] flags = new boolean[index + 1];
        Arrays.fill(flags, true);
        if (i == 1) {
            int j = 0;
            for (; j < index; j++) {
                //允许的告警编号
                if (Tools.findAllow(j + 1)) {
                    boolean currV = curr.getBool(j + 12);
                    if (currV) {
                        flags[j] = false;
                        recordAlarms = new TwinRecordAlarms();
                        recordAlarms.setAlarmType(j + 1);
                        recordAlarms.setStartTime(new Date(time));
                        alarmRecord.add(recordAlarms);
                    }
                }
            }
            //需要单独计算alarm27
            //数据位置
//            int pos = j + 12;
//            int curr27 = curr.getInt(pos);
//            if (curr27 != 0) {
//                flags[j] = false;
//                recordAlarms = new TwinRecordAlarms();
//                recordAlarms.setAlarmType(j + 1);
//                recordAlarms.setStartTime(new Date(time));
//                alarmRecord.add(recordAlarms);
//            }
        }
        int j = 0;
        for (; j < index; j++) {
            //允许的告警编号
            if (Tools.findAllow(j + 1)) {
                if (flags[j]) {
                    boolean currV = curr.getBool(j + 12);
                    boolean lastV = last.getBool(j + 12);
                    if (currV && !lastV) {
                        recordAlarms = new TwinRecordAlarms();
                        recordAlarms.setAlarmType(j + 1);
                        recordAlarms.setStartTime(new Date(time));
                        alarmRecord.add(recordAlarms);
                    }
                    if (!currV && lastV) {
                        recordAlarms = new TwinRecordAlarms();
                        recordAlarms.setAlarmType(j + 1);
                        recordAlarms.setEndTime(new Date(time));
                        alarmRecord.add(recordAlarms);
                    }
                }
            }
        }
//        if (flags[j]) {
//            //需要单独计算alarm27
//            //数据位置
//            int pos = j + 12;
//            int alarm27 = curr.getInt(pos);
//            //上面已经处理过i=0,所以这里i不可能等于0
//            int last27 = last.getInt(pos);
//            if (alarm27 != 0 && last27 == 0) {
//                recordAlarms = new TwinRecordAlarms();
//                recordAlarms.setAlarmType(j + 1);
//                recordAlarms.setStartTime(new Date(time));
//                alarmRecord.add(recordAlarms);
//            }
//            if (alarm27 == 0 && last27 != 0) {
//                recordAlarms = new TwinRecordAlarms();
//                recordAlarms.setAlarmType(j + 1);
//                recordAlarms.setEndTime(new Date(time));
//                alarmRecord.add(recordAlarms);
//            }
//        }
    }

    /**
     * 重量计算 提取公共方法
     */
    private void calcTotal(int j, float[] last, float[] first, float[] total, int lastMkz) {
        float v = last[j] - first[j];
        total[j] += v;
        if (j == 0) {
            //如果是米长，则计算重量，米长*米克重*2 2024-11-15
            float weight = BigDecimal.valueOf(v * lastMkz * 2 / 1000 / 1000).setScale(2, RoundingMode.HALF_UP).floatValue();
            total[2] = total[2] + weight;
        }
    }

}
