package com.m2pool.pool.task;

import com.m2pool.common.redis.service.RedisService;
import com.m2pool.pool.dto.MinerIncomeDto;
import com.m2pool.pool.entity.HourIncome;
import com.m2pool.pool.enums.RedisKey;
import com.m2pool.pool.mapper.IncomeMapper;
import com.m2pool.pool.mapper.PoolMapper;
import org.apache.ibatis.session.SqlSession;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
 * @Description 矿机收益 定时任务
 * @Date 2025/8/15 17:12
 * @Author yyb
 */
@Configuration
@EnableScheduling
public class IncomeTask {


    @Resource
    private IncomeMapper incomeMapper;


    @Resource
    private RedisService redisService;

    @Resource
    private PoolMapper poolMapper;

    private static final int BATCH_SIZE = 1000;

    private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors();
    private final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

    public void batchInsertHourIncomes(String coin, List<HourIncome> hourIncomes) {
        // 用于记录执行过程中的异常
        AtomicReference<Exception> exceptionRef = new AtomicReference<>();
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (int i = 0; i < hourIncomes.size(); i += BATCH_SIZE) {
            int toIndex = Math.min(i + BATCH_SIZE, hourIncomes.size());
            List<HourIncome> subList = hourIncomes.subList(i, toIndex);

            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    incomeMapper.batchInsertHourIncomes(coin, subList);
                } catch (Exception e) {
                    exceptionRef.set(e);
                    throw new RuntimeException(e);
                }
            }, executorService);

            futures.add(future);
        }
        // 等待所有任务完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        try {
            allFutures.get();
        } catch (InterruptedException | ExecutionException e) {
            // 如果有异常，将异常信息设置到 exceptionRef 中
            exceptionRef.set(e);
        }

        // 如果有异常，抛出异常触发事务回滚
        if (exceptionRef.get() != null) {
            System.out.println("批量插入数据时发生错误"+exceptionRef.get());
            throw new RuntimeException("批量插入数据时发生错误", exceptionRef.get());
        }
    }

    @Scheduled(cron = "5 21 0/1 * * ? ")
    public void nexaIncomeTask() {
        LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
        LocalDateTime end = now.minusHours(1);
        LocalDateTime start = now.minusHours(2);
        System.out.println("nexa 矿机收益入库开始"+ LocalDateTime.now());
        //1.获取m2pool中 1-2小时前的单个矿机算力
        List<MinerIncomeDto> nexaMinerIncomes = incomeMapper.getMinerAvgPower("nexa", start, end);
        //2.获取矿池nexa 总算力
        BigDecimal totalPower = nexaMinerIncomes.stream()
                .map(MinerIncomeDto::getAvgPower)
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //3.获取当前nexa币价（这个key是在6379 redis实例的 6 号数据库中，但是默认配置是0号库获取的币价一定为null）
        //String price = redisService.getCacheObject(RedisKey.NEXA_PRICE);
        //BigDecimal nexaPrice ;
        //if (price == null){
        //    nexaPrice = poolMapper.selectPrice("nexa");
        //}else{
        //    nexaPrice = new BigDecimal(price);
        //}
        BigDecimal nexaPrice = poolMapper.selectPrice("nexa");
        //4.获取nexa 1-2小时前的报块数
        BigDecimal nexaBlocks = incomeMapper.getVaildBlcoks("nexa", start, end);

        //5.计算单个矿机的收益
        List<HourIncome> totalIncomes = new ArrayList<>();
        nexaMinerIncomes.forEach(minerIncomeDto -> {
            BigDecimal income = nexaBlocks.multiply(minerIncomeDto.getAvgPower().divide(totalPower, 8, RoundingMode.HALF_UP));
            BigDecimal usdtIncome = income.multiply(nexaPrice);
            totalIncomes.add(HourIncome.builder()
                            .user(minerIncomeDto.getUser())
                            .miner(minerIncomeDto.getMiner())
                            .income(income)
                            .usdtIncome(usdtIncome)
                            .build());
        });
        //6.矿机收益入库
        batchInsertHourIncomes("nexa",totalIncomes);

            System.out.println("nexa 矿机收益入库完成"+ LocalDateTime.now());
    }
    @Scheduled(cron = "15 22 0/1 * * ? ")
    public void rxdIncomeTask() {
        LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
        LocalDateTime end = now.minusHours(1);
        LocalDateTime start = now.minusHours(2);
        System.out.println("rxd 矿机收益入库开始"+ LocalDateTime.now());
        //1.获取m2pool中 1-2小时前的单个矿机算力
        List<MinerIncomeDto> rxdMinerIncomes = incomeMapper.getMinerAvgPower("rxd", start, end);
        //2.获取矿池rxd总算力
        BigDecimal totalPower = rxdMinerIncomes.stream()
                .map(MinerIncomeDto::getAvgPower)
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //3.获取当前rxd币价
        //String price = redisService.getCacheObject(RedisKey.RXD_PRICE);
        //BigDecimal rxdPrice;
        //if (price == null){
        //    rxdPrice = poolMapper.selectPrice("rxd");
        //}else{
        //    rxdPrice = new BigDecimal(price);
        //}

        BigDecimal rxdPrice = poolMapper.selectPrice("rxd");
        //4.获取rxd 1-2小时前的报块数
        BigDecimal rxdBlocks = incomeMapper.getVaildBlcoks("rxd", start, end);

        //5.计算单个矿机的收益
        List<HourIncome> totalIncomes = new ArrayList<>();
        rxdMinerIncomes.forEach(minerIncomeDto -> {
            BigDecimal income = rxdBlocks.multiply(minerIncomeDto.getAvgPower().divide(totalPower, 8, RoundingMode.HALF_UP));
            BigDecimal usdtIncome = income.multiply(rxdPrice);
            totalIncomes.add(HourIncome.builder()
                    .user(minerIncomeDto.getUser())
                    .miner(minerIncomeDto.getMiner())
                    .income(income)
                    .usdtIncome(usdtIncome)
                    .build());
        });
        //6.矿机收益入库
        batchInsertHourIncomes("rxd",totalIncomes);

        System.out.println("rxd 矿机收益入库完成"+ LocalDateTime.now());
    }
    @Scheduled(cron = "25 23 0/1 * * ? ")
    public void monaIncomeTask() {
        LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
        LocalDateTime end = now.minusHours(1);
        LocalDateTime start = now.minusHours(2);
        System.out.println("mona 矿机收益入库开始"+ LocalDateTime.now());
        //1.获取m2pool中 1-2小时前的单个矿机算力
        List<MinerIncomeDto> monaMinerIncomes = incomeMapper.getMinerAvgPower("mona", start, end);
        //2.获取矿池mona总算力
        BigDecimal totalPower = monaMinerIncomes.stream()
                .map(MinerIncomeDto::getAvgPower)
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //3.获取当前mona币价
        //String price = redisService.getCacheObject(RedisKey.MONA_PRICE);
        //BigDecimal monaPrice;
        //if (price == null){
        //    monaPrice = poolMapper.selectPrice("mona");
        //}else{
        //    monaPrice = new BigDecimal(price);
        //}
        BigDecimal monaPrice = poolMapper.selectPrice("mona");
        //4.获取mona 1-2小时前的报块数
        BigDecimal monaBlocks = incomeMapper.getVaildBlcoks("rxd", start, end);

        //5.计算单个矿机的收益
        List<HourIncome> totalIncomes = new ArrayList<>();
        monaMinerIncomes.forEach(minerIncomeDto -> {
            BigDecimal income = monaBlocks.multiply(minerIncomeDto.getAvgPower().divide(totalPower, 8, RoundingMode.HALF_UP));
            BigDecimal usdtIncome = income.multiply(monaPrice);
            totalIncomes.add(HourIncome.builder()
                    .user(minerIncomeDto.getUser())
                    .miner(minerIncomeDto.getMiner())
                    .income(income)
                    .usdtIncome(usdtIncome)
                    .build());
        });
        //6.矿机收益入库
        batchInsertHourIncomes("mona",totalIncomes);

        System.out.println("mona 矿机收益入库完成"+ LocalDateTime.now());
    }
    @Scheduled(cron = "35 24 0/1 * * ? ")
    public void grsIncomeTask() {
        LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
        LocalDateTime end = now.minusHours(1);
        LocalDateTime start = now.minusHours(2);
        System.out.println("grs 矿机收益入库开始"+ LocalDateTime.now());
        //1.获取m2pool中 1-2小时前的单个矿机算力
        List<MinerIncomeDto> grsMinerIncomes = incomeMapper.getMinerAvgPower("rxd", start, end);
        //2.获取矿池grs总算力
        BigDecimal totalPower = grsMinerIncomes.stream()
                .map(MinerIncomeDto::getAvgPower)
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //3.获取当前grs币价
        //String price = redisService.getCacheObject(RedisKey.GRS_PRICE);
        //BigDecimal grsPrice ;
        //if (price == null){
        //    grsPrice = poolMapper.selectPrice("grs");
        //}else{
        //    grsPrice = new BigDecimal(price);
        //}
        BigDecimal grsPrice = poolMapper.selectPrice("grs");
        //4.获取rxd 1-2小时前的报块数
        BigDecimal grsBlocks = incomeMapper.getVaildBlcoks("grs", start, end);

        //5.计算单个矿机的收益
        List<HourIncome> totalIncomes = new ArrayList<>();
        grsMinerIncomes.forEach(minerIncomeDto -> {
            BigDecimal income = grsBlocks.multiply(minerIncomeDto.getAvgPower().divide(totalPower, 8, RoundingMode.HALF_UP));
            BigDecimal usdtIncome = income.multiply(grsPrice);
            totalIncomes.add(HourIncome.builder()
                    .user(minerIncomeDto.getUser())
                    .miner(minerIncomeDto.getMiner())
                    .income(income)
                    .usdtIncome(usdtIncome)
                    .build());
        });
        //6.矿机收益入库
        batchInsertHourIncomes("grs",totalIncomes);

        System.out.println("grs 矿机收益入库完成"+ LocalDateTime.now());
    }

    //TODO 门罗收益

}
