package com.njcn.oracle.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.extra.spring.SpringUtil;
import com.njcn.oracle.bo.param.DataAsynParam;
import com.njcn.oracle.bo.param.MigrationParam;
import com.njcn.oracle.bo.param.ServiceTypeEnum;
import com.njcn.oracle.bo.po.JobDetail;
import com.njcn.oracle.bo.po.JobDetailHours;
import com.njcn.oracle.bo.po.JobHistoryLog;
import com.njcn.oracle.mybatis.service.IReplenishMybatisService;
import com.njcn.oracle.service.IOracleService;
import com.njcn.oracle.service.JobDetailHoursService;
import com.njcn.oracle.service.JobDetailService;
import com.njcn.oracle.service.JobHistoryLogService;
import com.njcn.oracle.util.LocalDateUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import java.lang.ref.WeakReference;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Data
@Service
@RequiredArgsConstructor
@Slf4j
public class OracleServiceImpl implements IOracleService {

    private final static String PACKAGE_PREFIX = "com.njcn.oracle.service.impl.";
    private final static String PACKAGE_SUFFIX = "ServiceImpl";

    private final JobDetailService jobDetailService;
    private final JobHistoryLogService jobHistoryLogService;

    private final List<IReplenishMybatisService> services;

    private final JobDetailHoursService jobDetailHoursService;

    @Value("${business.slice:2}")
    private int slice;

    /**
     * 批量处理数据表的数据迁移
     *
     * @param dataAsynParam 业务参数
     */
    @Override
    @Async
    public void dataBacthSysc(DataAsynParam dataAsynParam) {
        Runtime runtime = Runtime.getRuntime();
        System.out.println("开始执行前总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
        System.out.println("开始执行前已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
        System.out.println("开始执行前空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");
        //目前且作为2片，后续将该属性提取到配置文件中
        List<String> tableNames = dataAsynParam.getTableNames();
        //嵌套循环，先循环指标，再循环日期
        tableNames.stream().forEach(tableName -> {
            IReplenishMybatisService executor;
            try {
                executor = (IReplenishMybatisService) SpringUtil.getBean(Class.forName(PACKAGE_PREFIX + tableName + PACKAGE_SUFFIX));
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            List<LocalDate> dateList = LocalDateUtil.getDateList(dataAsynParam.getStartTime(), dataAsynParam.getEndTime());
            for (LocalDate date : dateList) {

                //添加记录批处理日志，执行为批处理且最后一张表执行完记录
                if(dataAsynParam.getExcuteType()==2&&Objects.equals(tableName, ServiceTypeEnum.LIMITTARGET.getCode())){
                    JobHistoryLog jobHistoryLog = new JobHistoryLog(date,LocalDateTime.now());
                    jobHistoryLogService.save(jobHistoryLog);
                }
                //日志记录
                JobDetail jobDetail = jobDetailService.select(tableName, date);
                if (Objects.nonNull(jobDetail) && (jobDetail.getState() == 1 || jobDetail.getState() == 0)) {
                    //如果该指标当前天已执行或正在执行，直接跳出循环
                    break;
                }
                if (Objects.isNull(jobDetail)) {
                    jobDetail = new JobDetail(tableName, date, 0, 0, LocalDateTime.now());
                    jobDetailService.save(jobDetail);
                }

                //程序监听
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();
                //分片下，每段时间的小时数
                int sliceHour = 24 / slice;
                for (int i = 0; i < slice; i++) {
                    String dateStr = LocalDateTimeUtil.format(date, DatePattern.NORM_DATE_PATTERN);
                    LocalDateTime startTime = LocalDateTimeUtil.parse(dateStr + StrPool.C_SPACE + (sliceHour * i < 10 ? "0" + sliceHour * i : sliceHour * i) + ":00:00", DatePattern.NORM_DATETIME_PATTERN);
                    LocalDateTime endTime = LocalDateTimeUtil.parse(dateStr + StrPool.C_SPACE + (sliceHour * (i + 1) - 1 < 10 ? "0" + (sliceHour * (i + 1) - 1) : sliceHour * (i + 1) - 1) + ":59:59", DatePattern.NORM_DATETIME_PATTERN);
                    //查询该时区的数据，并准备入库
                    MigrationParam migration = new MigrationParam();
                    migration.setStartTime(startTime);
                    migration.setEndTime(endTime);
                    //采用弱引用接受，后续手动调用gc后，会清空该对象
                    WeakReference<List> weakReferenceData = new WeakReference<>(executor.queryData(migration));
                    int size = 0;
                    if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
                        size = weakReferenceData.get().size();
                    }
                    System.out.println(tableName + "查到" + size + "条数据后总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
                    System.out.println(tableName + "查到" + size + "条数据后已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
                    System.out.println(tableName + "查到" + size + "条数据后空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");
                    try {
                        if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
                            //执行目标库的数据处理
                            executor.clearTargetData(migration);
                            executor.insertBatchByDB(weakReferenceData.get());
                            //最后一片时修改状态
                        }
                        //手动执行GC
                        System.gc();
                        if (i == 0) {
                            jobDetail.setRowCount(0);
                        }
                        jobDetail.setRowCount(jobDetail.getRowCount() + size);
                        if (i + 1 == slice) {
                            stopWatch.stop();
                            jobDetail.setState(1);
                            jobDetail.setDuration(stopWatch.getTotalTimeSeconds());
                        }
                        jobDetail.setUpdateTime(LocalDateTime.now());
                        jobDetailService.updateByMultiId(jobDetail);
                    } catch (Exception exception) {
                        exception.printStackTrace();

                        jobDetail.setState(2);
                        jobDetail.setUpdateTime(LocalDateTime.now());
                        jobDetailService.updateByMultiId(jobDetail);
                    }


                    System.out.println("执行后总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
                    System.out.println("执行后已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
                    System.out.println("执行后空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");
                }


            }


        });
    }

    @Override
    @Async
    public void dataSyncHours(DataAsynParam dataAsynParam) {
        Runtime runtime = Runtime.getRuntime();
        String tablename;
        System.out.println("开始执行前总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
        System.out.println("开始执行前已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
        System.out.println("开始执行前空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");

        LocalDateTime startDateTime = dataAsynParam.getStartDateTime();
        LocalDateTime endDateTime = dataAsynParam.getEndDateTime();
        while (endDateTime.isBefore(startDateTime)) {
            startDateTime = startDateTime.minusHours(1);
            log.info("执行"+startDateTime+"时刻数据");
            for (IReplenishMybatisService service : services) {
                tablename = service.getClass().getName().split(PACKAGE_PREFIX+"|"+PACKAGE_SUFFIX)[1];

                //日志记录
                JobDetailHours jobDetailHours = jobDetailHoursService.lambdaQuery().eq(JobDetailHours::getTableName, tablename)
                        .eq(JobDetailHours::getExcuteDate, startDateTime)
                        .one();
                if (Objects.nonNull(jobDetailHours) && (jobDetailHours.getState() == 1 || jobDetailHours.getState() == 0|| jobDetailHours.getState() == 2)) {
                    //如果该指标当前时间段已执行或正在执行，直接跳出循环
                    break;
                }
                if (Objects.isNull(jobDetailHours)) {
                    jobDetailHours = new JobDetailHours(tablename, startDateTime, 0, 0, LocalDateTime.now());
                    jobDetailHoursService.save(jobDetailHours);
                }
                //程序监听
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();

                MigrationParam migration = new MigrationParam();
                migration.setStartTime(startDateTime);
                //between是闭区间，所以这里加一小时减1秒，正好执行一个小时数据
                migration.setEndTime(startDateTime.minusHours(-1).minusSeconds(1));
                //采用弱引用接受，后续手动调用gc后，会清空该对象
                WeakReference<List> weakReferenceData = new WeakReference<>(service.queryData(migration));

                int size = 0;
                if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
                    size = weakReferenceData.get().size();
                }
                System.out.println(""+ "查到" + size + "条数据后总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
                System.out.println("" + "查到" + size + "条数据后已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
                System.out.println("" + "查到" + size + "条数据后空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");
                try {
                    if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
                        //执行目标库的数据处理
                        service.clearTargetData(migration);
                        service.insertBatchByDB(weakReferenceData.get());
                        //最后一片时修改状态
                    }
                    //手动执行GC
                    System.gc();
                    stopWatch.stop();
                    jobDetailHours.setRowCount(size);
                    jobDetailHours.setState(1);
                    jobDetailHours.setDuration(stopWatch.getTotalTimeSeconds());
                    jobDetailHoursService.updateByMultiId(jobDetailHours);

                } catch (Exception exception) {
                    exception.printStackTrace();
                    jobDetailHours.setState(2);
                    jobDetailHours.setUpdateTime(LocalDateTime.now());
                    jobDetailHoursService.updateByMultiId(jobDetailHours);
                }


                System.out.println("执行后总堆内存为：" + runtime.totalMemory() / (1024 * 1024) + " MB");
                System.out.println("执行后已用堆内存为：" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
                System.out.println("执行后空闲堆内存为：" + runtime.freeMemory() / (1024 * 1024) + " MB");
            }



        }
    }
}
