package com.tofly.scada.service.impl;

import cn.hutool.core.date.DateUtil;
import com.tofly.scada.entity.Scada;
import com.tofly.scada.mapper.ScadaMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author haiqiu
 * @date 2022/7/25
 */
@Component
@Slf4j
public class ScadaApplicationRunner implements ApplicationRunner {

    @Autowired
    private ScadaMapper scadaMapper;

    @Autowired
    private ScadaServiceImpl scadaService;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        //进行scada实时表记录
//        autoMonitoringData();
        //月份数据中断补偿操作
        //autoMouthScada(null);
    }


    @Async
    public void autoMonitoringData() {
        //进行scada实时表记录
        List<Scada> listData = scadaMapper.getListData();
        log.info("获取到第一次启动项目最新监控实时数据{}", listData);
        if (!CollectionUtils.isEmpty(listData)) {
            log.info("获取到第一次启动项目最新监控实时数据{}", listData);
            for (Scada scada : listData) {
                if (StringUtils.hasText(scada.getValue()) && scada.getValue().startsWith(".")){
                    scada.setValue("0"+scada.getValue());
                }
                Scada monitoring = scadaMapper.selectDataByMonitoring(scada.getCode());
                log.info("查询存在的实时数据{}", monitoring);
                if (ObjectUtils.isEmpty(monitoring)) {
                    scadaMapper.insertMonitoring(scada);
                    log.info("无实时数据，进行新增{}", scada);
                } else {
                    scadaMapper.updateMonitoring(scada);
                    log.info("有实时数据，进行更新{}", scada);
                }
            }
        } else {
            log.info("无数据，不进行同步{}", listData);
        }
    }


    //@Scheduled(cron = "0 0/30 * * * ?")
    public void timingMonitoringData(){
        autoMonitoringData();
    }

    @Async
    @Transactional
    public void autoMouthScada(Date time) {
        //整个日历调整时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time == null ? new Date() : time);
        log.info("同步scada历史数据到月份表：正在进行时间:"
                + DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss") + "调整");
        //倒退一个月开始时间
//        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();
        String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        //倒退一个月最后一天时间
        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        log.info("同步scada历史数据到月份表：正在进行历史数据查询....");
        //查询历史数据
        List<String> codes = scadaService.getCodes(null);
        if (!CollectionUtils.isEmpty(codes)) {
            //存储到分表月份
            String tableName = scadaService.createMonths(startTime);
            log.info("创建月份表成功：-----{}-----", tableName);
            //删除旧数据
            int delete = scadaMapper.deleteFromTableName(tableName, start, end);
            log.info("成功删除旧数据：{}条", delete);

            ExecutorService executorService = Executors.newFixedThreadPool(codes.size());
            CountDownLatch countDownLatch = new CountDownLatch(codes.size());
            for (String code : codes) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        List<Scada> scadas = scadaService.getScadasDatas(code, startTime, endTime);
                        if (!CollectionUtils.isEmpty(scadas)) {
                            log.info("历史月份：{}不为空，正在新增数据code:{}", start, code);
                            //添加新数据
                            int insertMouth = insertMouth(scadas, tableName);
                            log.info("新增合计数据：{}条", insertMouth);
                        }
                        countDownLatch.countDown();
                    }
                });
            }
            executorService.shutdown();
        }

    }


    /**
     * 非异步多线程处理
     *
     * @param time
     */
    @Transactional
    public void autoMouthScadaSlow(Date time) {
        //整个日历调整时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time == null ? new Date() : time);
        log.info("同步scada历史数据到月份表：正在进行时间:"
                + DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss") + "调整");
        //倒退一个月开始时间
//        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();
        String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        //倒退一个月最后一天时间
        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        log.info("同步scada历史数据到月份表：正在进行历史数据查询....");
        //查询历史数据
        List<String> codes = scadaService.getCodes(null);
        if (!CollectionUtils.isEmpty(codes)) {
            //存储到分表月份
            String tableName = scadaService.createMonths(startTime);
            log.info("创建月份表成功：-----{}-----", tableName);
            //删除旧数据
            int delete = scadaMapper.deleteFromTableName(tableName, start, end);
            log.info("成功删除旧数据：{}条", delete);

            for (String code : codes) {
                List<Scada> scadas = scadaService.getScadasDatas(code, startTime, endTime);
                if (!CollectionUtils.isEmpty(scadas)) {
                    log.info("历史月份：{}不为空，正在新增数据code:{}", start, code);
                    //添加新数据
                    int insertMouth = insertMouth(scadas, tableName);
                    log.info("新增合计数据：{}条", insertMouth);
                }
            }
        }
    }

    /**
     * 凌晨4点同步更新历史数据月表数据
     */
    //@Scheduled(cron = "0 0 4 * * ? ")
    public void timingMouthScadaSlow(){
        autoMouthScadaSlow(new Date());
    }


    /**
     * 截取数组长度批量插入
     * @param scadas 数据集合
     * @param tableName 表名
     * @return 成功条数
     */
    public int insertMouth(List<Scada> scadas, String tableName) {
        int size = scadas.size();
        int limit = 1000;
        int startIndex = 0;
        int endIndex;
        if (size <= limit){
            //添加新数据
            int insertMouth = scadaMapper.insertMouth(scadas, tableName);
            log.info("新增数据：{}条", insertMouth);
        }else {
            while (startIndex<size){
                //结束索引范围
                endIndex = startIndex + limit;
                //最后索引不能超过本身长度
                endIndex = endIndex > size ? size : endIndex;
                //截取数据
                List<Scada> scadaList = scadas.subList(startIndex, endIndex);
                //下次索引位置
                startIndex = endIndex;
                //添加新数据
                int insertMouth = scadaMapper.insertMouth(scadaList, tableName);
                log.info("新增数据：{}条", insertMouth);
            }
        }
        return size;
    }

}