package com.jjt.calc.service.impl;

import com.jjt.biz.domain.TwinDevice;
import com.jjt.calc.domain.TwinCalcHour;
import com.jjt.calc.domain.TwinCalcStop;
import com.jjt.calc.domain.TwinRecordStop;
import com.jjt.calc.mapper.TwinCalcHourMapper;
import com.jjt.calc.mapper.TwinCalcStopMapper;
import com.jjt.calc.service.ITwinCalcHourService;
import com.jjt.calc.service.ITwinCalcStopService;
import com.jjt.calc.service.ITwinRecordStopService;
import com.jjt.common.utils.DateUtils;
import com.jjt.common.utils.StringUtils;
import com.jjt.common.utils.bean.BeanUtils;
import com.jjt.system.service.ISysConfigService;
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.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 停机数据统计Service业务层处理
 *
 * @author wukai
 * @date 2025-01-18
 */
@Service
public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
    @Resource
    private TwinCalcStopMapper twinCalcStopMapper;
    @Resource
    private ISysConfigService configService;
    @Resource
    private ITwinRecordStopService stopService;
    @Resource
    private SqlSessionFactory factory;
    @Resource
    private ITwinCalcHourService calcHourService;

    /**
     * 查询停机数据统计
     *
     * @param id 停机数据统计主键
     * @return 停机数据统计
     */
    @Override
    public TwinCalcStop selectTwinCalcStopById(Long id) {
        return twinCalcStopMapper.selectTwinCalcStopById(id);
    }

    /**
     * 查询停机数据统计列表
     *
     * @param twinCalcStop 停机数据统计
     * @return 停机数据统计
     */
    @Override
    public List<TwinCalcStop> selectTwinCalcStopList(TwinCalcStop twinCalcStop) {
        return twinCalcStopMapper.selectTwinCalcStopList(twinCalcStop);
    }

    /**
     * 新增停机数据统计
     *
     * @param twinCalcStop 停机数据统计
     * @return 结果
     */
    @Override
    public int insertTwinCalcStop(TwinCalcStop twinCalcStop) {
        return twinCalcStopMapper.insertTwinCalcStop(twinCalcStop);
    }

    /**
     * 修改停机数据统计
     *
     * @param twinCalcStop 停机数据统计
     * @return 结果
     */
    @Override
    public int updateTwinCalcStop(TwinCalcStop twinCalcStop) {
        return twinCalcStopMapper.updateTwinCalcStop(twinCalcStop);
    }

    /**
     * 批量删除停机数据统计
     *
     * @param ids 需要删除的停机数据统计主键
     * @return 结果
     */
    @Override
    public int deleteTwinCalcStopByIds(Long[] ids) {
        return twinCalcStopMapper.deleteTwinCalcStopByIds(ids);
    }

    /**
     * 删除停机数据统计信息
     *
     * @param id 停机数据统计主键
     * @return 结果
     */
    @Override
    public int deleteTwinCalcStopById(Long id) {
        return twinCalcStopMapper.deleteTwinCalcStopById(id);
    }

    /**
     * 停机数据治理，从停机记录表到停机统计表中
     *
     * @param startTime  开始时间
     * @param endTime    结束时间
     * @param deviceList 设备列表
     */
    @Override
    public void process(LocalDateTime startTime, LocalDateTime endTime, List<TwinDevice> deviceList) {
        String config = configService.selectConfigByKey("data.legal.time");
        int legalTime = Integer.parseInt(config) * 1000;

        Date date = DateUtils.toDate(startTime.toLocalDate());
        Date startDate = DateUtils.toDate(startTime);
        Date endDate = DateUtils.toDate(endTime);


        //查询上次未统计完整的数据
        TwinCalcStop calcSearch = new TwinCalcStop();
        calcSearch.setCalcStatus("1");
        List<TwinCalcStop> oldList = selectTwinCalcStopList(calcSearch);
        Map<Long, List<TwinCalcStop>> oldMap = oldList.stream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));

        //查询本次数据
        TwinRecordStop recordSearch = new TwinRecordStop();
        recordSearch.setDataDate(date);
        recordSearch.setHour(startTime.getHour());
        List<TwinRecordStop> newList = stopService.selectTwinRecordStopList(recordSearch);
        Map<Long, List<TwinRecordStop>> newMap = newList.stream().collect(Collectors.groupingBy(TwinRecordStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));

        //查询统计数据
        TwinCalcHour hourSearch = new TwinCalcHour();
        hourSearch.setDataDate(date);
        hourSearch.setHour(startTime.getHour());
        List<TwinCalcHour> hourList = calcHourService.selectTwinCalcHourList(hourSearch);
        //处理设备数据
        List<TwinCalcStop> insertList = new ArrayList<>();
        List<TwinCalcStop> updateList = new ArrayList<>();
        List<TwinCalcHour> calcStopTimeList = new ArrayList<>();

        List<TwinRecordStop> recordList = new ArrayList<>();
        for (TwinDevice device : deviceList) {
            //先处理上次未统计完整的数据
            List<TwinRecordStop> list = new ArrayList<>();
            if (oldMap.get(device.getDeviceId()) != null) {
                for (TwinCalcStop old : oldMap.get(device.getDeviceId())) {
                    if (!old.getEndTime().after(endDate)) {
                        TwinRecordStop stop = new TwinRecordStop();
                        BeanUtils.copyProperties(old, stop);
                        //这里存ID，方便后面需要删除，判断是否之前的记录，则直接判断remark是否为空即可
                        stop.setRemark(old.getId() + "");
                        list.add(stop);
                    }
                }
            }
            //再处理本次数据，2次结合
            if (newMap.get(device.getDeviceId()) != null) {
                list.addAll(newMap.get(device.getDeviceId()));
            }

            //1.先循环一轮，处理记录合并
            List<TwinRecordStop> list1 = mergeList(list, legalTime);
            List<TwinRecordStop> list2 = new ArrayList<>();
            //2.再循环合并后的记录，删除无效数据
            for (TwinRecordStop stop : list1) {
                if ((stop.getEndTime().getTime() - stop.getStartTime().getTime()) > legalTime) {
                    list2.add(stop);

                }
            }
            //3.还要循环一轮，进行记录合并
            recordList.addAll(mergeList(list2, legalTime));
        }
        for (TwinRecordStop stop : recordList) {
            //2.复制记录
            TwinCalcStop v = new TwinCalcStop();
            BeanUtils.copyProperties(stop, v);

            TwinCalcHour calcHour = new TwinCalcHour();
            //1.计算停机时长
            if (stop.getStartTime().before(startDate)) {
                stop.setStartTime(startDate);
            }
            long stopTime = stop.getEndTime().getTime() - stop.getStartTime().getTime();

            calcHour.setDeviceId(stop.getDeviceId());
            calcHour.setCloseTime(stopTime);
            calcStopTimeList.add(calcHour);

            if (StringUtils.isNotEmpty(stop.getRemark())) {
                //需要判断 这次是否还是未处理完成的数据，如果结束时间小于数据结束时间，则完成
                if (v.getEndTime().before(endDate)) {
                    v.setCalcStatus("0");
                    v.setRemark("");
                }
                updateList.add(v);
            } else {
                v.setId(null);
                insertList.add(v);
            }
        }
        //按ID分组，并统计停机时间
        Map<Long, Long> resultMap = calcStopTimeList.stream().collect(Collectors.groupingBy(TwinCalcHour::getDeviceId, Collectors.reducing(0L, TwinCalcHour::getCloseTime, Long::sum)));
        for (TwinCalcHour hour : hourList) {
            Long closeTime = 0L;
            if (resultMap.get(hour.getDeviceId()) != null) {
                closeTime = resultMap.get(hour.getDeviceId());
                closeTime = Math.min(closeTime / 1000, 3600);
            }

            Long openTime = 3600 - closeTime;
            hour.setCloseTime(closeTime);
            hour.setOpenTime(openTime);
        }

        try (SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false)) {
            TwinCalcStopMapper mapper = sqlSession.getMapper(TwinCalcStopMapper.class);
            if (insertList.size() > 0) {
                insertList.forEach(mapper::insertTwinCalcStop);
            }
            if (updateList.size() > 0) {
                updateList.forEach(mapper::updateTwinCalcStop);
            }

            TwinCalcHourMapper hourMapper = sqlSession.getMapper(TwinCalcHourMapper.class);
            hourList.forEach(hourMapper::updateTwinCalcHour);
            sqlSession.commit();
        }

        //统计完成删除记录数据，避免记录超长
        stopService.deleteTwinRecordStop(date, startTime.getHour());
    }

    /**
     * 合并数据记录
     */
    private List<TwinRecordStop> mergeList(List<TwinRecordStop> list, int legalTime) {
        List<TwinRecordStop> result = new ArrayList<>();
        TwinRecordStop lastStop = null;

        for (TwinRecordStop stop : list) {
            if (lastStop != null && stop.getStopType().equals(lastStop.getStopType()) && (stop.getStartTime().getTime() - lastStop.getEndTime().getTime()) < legalTime) {
                //判断同一个类型，并且下一条记录的开始时间-上一条记录的结束时间 小于数据合法时间
                lastStop.setEndTime(stop.getEndTime());
                //需要设置统计未完成状态 为当前记录的
                lastStop.setCalcStatus(stop.getCalcStatus());
            } else {
                result.add(stop);
                lastStop = stop;
            }
        }
        return result;
    }


    /**
     * 按开始和结束时间查询数据
     *
     * @param sTime 开始时间
     * @param eTime 结束时间
     * @return 查询列表
     */
    @Override
    public List<TwinCalcStop> selectTwinCalcStopListByDate(Date sTime, Date eTime) {
        TwinCalcStop stop = new TwinCalcStop();
        Map<String, Object> params = new HashMap<>(16);
        params.put("sTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sTime));
        params.put("eTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, eTime));
        stop.setParams(params);
        return selectTwinCalcStopList(stop);
    }

    /**
     * 按开始和结束时间查询数据(生产天）
     *
     * @param sTime 开始时间
     * @param eTime 结束时间
     * @return 查询列表
     */
    @Override
    public List<TwinCalcStop> selectTwinCalcStopListByDate(Date sTime, Date eTime, int type) {
        TwinCalcStop stop = new TwinCalcStop();
        stop.setStopType(type);
        Map<String, Object> params = new HashMap<>(16);
        params.put("sTime", sTime);
        params.put("eTime", eTime);
        stop.setParams(params);
        return selectTwinCalcStopList(stop);
    }
}
