package com.jjt.rz.service.impl;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.jjt.common.core.redis.RedisCache;
import com.jjt.common.utils.DateUtils;
import com.jjt.common.utils.StringUtils;
import com.jjt.rz.domain.TwinCalcHourRz;
import com.jjt.rz.domain.TwinDeviceRz;
import com.jjt.rz.domain.TwinDeviceType;
import com.jjt.rz.domain.TwinDeviceTypeData;
import com.jjt.rz.mapper.TwinCalcHourRzMapper;
import com.jjt.rz.service.ITwinCalcHourRzService;
import com.jjt.rz.service.ITwinDeviceRzService;
import com.jjt.rz.service.ITwinDeviceTypeService;
import com.jjt.utils.IotService;
import com.jjt.utils.Tools;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 染整线小时工艺参数Service业务层处理
 *
 * @author wukai
 * @date 2025-06-03
 */
@Service
@Slf4j
public class TwinCalcHourRzServiceImpl implements ITwinCalcHourRzService {
    @Resource
    private TwinCalcHourRzMapper twinCalcHourRzMapper;
    @Resource
    private IotService iotService;
    @Resource
    private ITwinDeviceRzService rzService;
    @Resource
    private ITwinDeviceTypeService typeService;
    @Resource
    private SqlSessionFactory factory;
    @Resource
    private RedisCache redisCache;


    /**
     * 查询染整线小时工艺参数
     *
     * @param id 染整线小时工艺参数主键
     * @return 染整线小时工艺参数
     */
    @Override
    public TwinCalcHourRz selectTwinCalcHourRzById(Long id) {
        return twinCalcHourRzMapper.selectTwinCalcHourRzById(id);
    }

    /**
     * 查询染整线小时工艺参数列表
     *
     * @param twinCalcHourRz 染整线小时工艺参数
     * @return 染整线小时工艺参数
     */
    @Override
    public List<TwinCalcHourRz> selectTwinCalcHourRzList(TwinCalcHourRz twinCalcHourRz) {
        return twinCalcHourRzMapper.selectTwinCalcHourRzList(twinCalcHourRz);
    }

    /**
     * 新增染整线小时工艺参数
     *
     * @param twinCalcHourRz 染整线小时工艺参数
     * @return 结果
     */
    @Override
    public int insertTwinCalcHourRz(TwinCalcHourRz twinCalcHourRz) {
        return twinCalcHourRzMapper.insertTwinCalcHourRz(twinCalcHourRz);
    }

    /**
     * 修改染整线小时工艺参数
     *
     * @param twinCalcHourRz 染整线小时工艺参数
     * @return 结果
     */
    @Override
    public int updateTwinCalcHourRz(TwinCalcHourRz twinCalcHourRz) {
        return twinCalcHourRzMapper.updateTwinCalcHourRz(twinCalcHourRz);
    }

    /**
     * 批量删除染整线小时工艺参数
     *
     * @param ids 需要删除的染整线小时工艺参数主键
     * @return 结果
     */
    @Override
    public int deleteTwinCalcHourRzByIds(Long[] ids) {
        return twinCalcHourRzMapper.deleteTwinCalcHourRzByIds(ids);
    }

    /**
     * 删除染整线小时工艺参数信息
     *
     * @param id 染整线小时工艺参数主键
     * @return 结果
     */
    @Override
    public int deleteTwinCalcHourRzById(Long id) {
        return twinCalcHourRzMapper.deleteTwinCalcHourRzById(id);
    }

    /**
     * 获取上一时段数据
     */
    @Override
    public void last() {
        LocalDateTime ldt = Tools.currWholeTime();
        //上一个小时
        ldt = ldt.minusHours(1);
        LocalDateTime start = ldt;
        LocalDateTime end = start.plusHours(1);
        hour(start, end);
    }

    /**
     * 获取当前开机状态
     */
    @Override
    public Map<Integer, Pair<Integer, Integer>> curr() {
        List<TwinDeviceRz> devices = rzService.selectTwinDeviceRzList(new TwinDeviceRz());
        process(devices, null, null);
        Map<String, List<TwinDeviceRz>> lineMap = devices.stream().collect(Collectors.groupingBy(TwinDeviceRz::getLine));
        Map<Integer, Pair<Integer, Integer>> map = new HashMap<>();
        lineMap.forEach((line, list) -> {
            long onlineCount = list.stream().filter(TwinDeviceRz::getOnline).count();
            map.put(Integer.parseInt(line), new Pair<>(list.size() + 1, (int) onlineCount + 1));
        });
        return map;
    }

    /**
     * 按小时获取数据
     *
     * @param start 开始时间
     * @param end   结束时间
     */
    @Override
    public void hour(LocalDateTime start, LocalDateTime end) {
        // 将开始时间转换为Date类型，用于后续查询
        Date date = Date.from(start.toLocalDate().atStartOfDay(ZoneOffset.of("+8")).toInstant());


        // 删除已存在的小时级计算结果，避免重复计算
        deleteTwinCalcHourRz(start.toLocalDate(), start.getHour());

        // 获取设备列表
        List<TwinDeviceRz> list = rzService.selectTwinDeviceRzList(new TwinDeviceRz());
        process(list, start, end);

        // 构建最终结果列表
        List<TwinCalcHourRz> rzList = new ArrayList<>();
        list.forEach(item -> {
            TwinCalcHourRz calc = new TwinCalcHourRz();
            calc.setDeviceId(item.getDeviceId());
            calc.setDataDate(date);
            calc.setHour(start.getHour());
            if (item.getOnline()) {
                calc.setOnline("Y");
            } else {
                calc.setOnline("N");
            }
            calc.setData(JSON.toJSONString(item.getParams()));
            rzList.add(calc);
        });

        // 批量插入计算结果
        try (SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false)) {
            if (rzList.size() > 0) {
                TwinCalcHourRzMapper mapper = sqlSession.getMapper(TwinCalcHourRzMapper.class);
                rzList.forEach(mapper::insertTwinCalcHourRz);
                sqlSession.commit();
            }
        }
    }

    private void process(List<TwinDeviceRz> list, LocalDateTime start, LocalDateTime end) {
        boolean flag = start != null && end != null;
        // 获取设备类型列表
        List<TwinDeviceType> typeList = typeService.selectTwinDeviceTypeAll();
        // 使用设备类型ID作为键，设备类型作为值，创建设备类型映射
        Map<Long, TwinDeviceType> typeMap = new HashMap<>(16);
        // 存储明细数据，使用设备代码和数据代码作为键
        Map<String, TwinDeviceTypeData> detailMap = new HashMap<>(16);
        typeList.forEach(item -> typeMap.put(item.getTypeId(), item));

        // 初始化存储查询结果的列表和映射
        List<String> strList = new ArrayList<>();
        Map<String, TwinDeviceRz> rzMap = new HashMap<>(16);

        // 这个映射用于保存数据是属于 设定值 还是mes 还是实际值
        Map<String, Pair<String, String>> clsMap = new HashMap<>(16);
        // 查询状态结尾部分
        String statusSuffix = ".Capacity_data_1";
        list.forEach(item -> {
            String deviceCode = item.getDeviceCode();
            String str = deviceCode + statusSuffix;
            item.setOnline(false);
            rzMap.put(deviceCode, item);
            strList.add(str);

            TwinDeviceType type = typeMap.get(item.getTypeId());
            if (type != null && type.getTwinDeviceTypeDataList() != null) {
                type.getTwinDeviceTypeDataList().forEach(detail -> {
                    String suffix1 = "." + detail.getDataCode();
                    String key1 = deviceCode + suffix1;
                    strList.add(key1);
                    clsMap.put(key1, new Pair<>("set", detail.getDataName()));
                    detailMap.put(key1, detail);

                    if (StringUtils.isNotEmpty(detail.getMesCode())) {
                        suffix1 = "." + detail.getMesCode();
                        String key2 = deviceCode + suffix1;
                        strList.add(key2);
                        clsMap.put(key2, new Pair<>("mes", detail.getDataName()));
                        detailMap.put(key2, detail);
                    }

                    if (StringUtils.isNotEmpty(detail.getActCode())) {
                        JSONArray array = JSONUtil.parseArray(detail.getActCode());
                        for (int j = 0; j < array.size(); j++) {
                            JSONObject jsonObject = array.getJSONObject(j);
                            String code = jsonObject.getStr("code");
                            String name = jsonObject.getStr("name");
//                            if (StringUtils.isEmpty(name)) {
//                            name = detail.getDataName();
//                            }
                            String suffix2 = "." + code;
                            String key3 = deviceCode + suffix2;
                            strList.add(key3);
                            clsMap.put(key3, new Pair<>("act", name));
                            detailMap.put(key3, detail);
                        }
                    }
                });
            }
        });

        // 构建查询SQL
        String sql = "select last %s from root.tl.suxi";
        sql = String.format(sql, String.join(",", strList));
        if (flag) {
            // 将开始和结束时间转换为毫秒，用于后续查询
            Long startTime = start.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            Long endTime = end.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            sql += String.format(" where time>%s and time <=%s", startTime, endTime);
        }

        // 执行查询并处理结果
        JSONObject jsonObject = iotService.query(sql);
        JSONObject data = jsonObject.getJSONObject("data");
        JSONArray values = data.getJSONArray("values");
        JSONArray columnNames = data.getJSONArray("columnNames");
        JSONArray timestamps = data.getJSONArray("timestamps");
        for (int i = 0; i < values.size(); i++) {
            JSONArray da = values.getJSONArray(i);
            String type = da.getStr(0);
            String[] tmp = type.split("\\.");
            String deviceCode = tmp[3];
            String dataCode = tmp[4];
            String value = da.getStr(1);
            TwinDeviceRz rz = rzMap.get(deviceCode);
            if (rz != null) {
                Map<String, Object> params = rz.getParams();
                if (params == null) {
                    params = new HashMap<>();
                    rz.setParams(params);
                }
                // 判断状态
                if (type.contains(statusSuffix)) {
                    if (rz.getTypeId() == 3 || rz.getTypeId() == 5 || rz.getTypeId() == 9) {
                        // 双棍烫光和两棍刷毛需要按照机器状态来判断是否开机
                        //=1 设备故障停机  ，=2 人工停机 ，=3是缺布/断布停机 ，=4满布停机，=5设备上电待机中  =6设备自动运行中，=7设备过站中
                        if ("6".equals(value)) {
                            rz.setOnline(true);
                        }
                    } else {
                        // 其他类型只需要有值就表示开机
                        rz.setOnline(true);
                    }
                } else {
                    // 工艺参数数据获取
                    String key = deviceCode + "." + dataCode;
                    TwinDeviceTypeData detail = detailMap.get(key);
                    Pair<String, String> clsPair = clsMap.get(key);
                    Object obj = params.get(detail.getDataName());
                    Map<String, Object> subMap = new HashMap<>(16);
                    if (obj == null) {
                        params.put(detail.getDataName(), subMap);
                    } else {
                        subMap = (Map<String, Object>) obj;
                    }
                    if ("act".equals(clsPair.getKey())) {
                        List<Map<String, String>> actList = (List<Map<String, String>>) subMap.get(clsPair.getKey());
                        if (actList == null) {
                            actList = new ArrayList<>();
                        }
                        Map<String, String> actMap = new HashMap<>(2);
                        actMap.put("name", clsPair.getValue());
                        actMap.put("value", value);
                        actList.add(actMap);
                        subMap.put(clsPair.getKey(), actList);
                    } else {
                        subMap.put(clsPair.getKey(), value);
                    }
                }
            }
        }
    }


    /**
     * 按日期和时间删除数据
     *
     * @param date 日期
     * @param hour 时间
     */
    @Override
    public void deleteTwinCalcHourRz(LocalDate date, int hour) {
        twinCalcHourRzMapper.deleteTwinCalcHourRz(date.toString(), hour);
    }

    /**
     * 补录数据至当前时间
     */
    @Override
    public void calc2Curr() {
        TwinCalcHourRz last = lastRecord();
        LocalDate localDate = DateUtils.toLocalDate(last.getDataDate());
        LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(last.getHour() + 1);
        LocalDateTime end = start.plusHours(1);

        LocalDateTime stop = Tools.currWholeTime();
        while (!end.isAfter(stop)) {
            hour(start, end);
            log.info("补录数据===========start:{},end:{},stop:{}", start, end, stop);
            start = end;
            end = end.plusHours(1);
        }
    }

    private TwinCalcHourRz lastRecord() {
        return twinCalcHourRzMapper.lastRecord();
    }
}
