package com.m2pool.pool.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.github.pagehelper.PageHelper;
import com.m2pool.common.core.text.Convert;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.datasource.annotation.UserDB;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.pool.dto.*;
import com.m2pool.pool.entity.BlockInfo;
import com.m2pool.pool.entity.PoolPower;
import com.m2pool.pool.enums.*;
import com.m2pool.pool.mapper.PoolMapper;
import com.m2pool.pool.mapper.UserAccountMapper;
import com.m2pool.pool.service.PoolService;
import com.m2pool.pool.task.DataTask;
import com.m2pool.pool.utils.EnumUtils;
import com.m2pool.pool.utils.NodeRpc;
import com.m2pool.pool.utils.PowerUnitUtils;
import com.m2pool.pool.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description TODO
 * @Date 2024/6/14 14:29
 * @Author dy
 */
@Service
public class PoolServiceImpl implements PoolService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private PoolMapper poolMapper;

    @Autowired
    private UserAccountMapper uaMapper;


    @Override
    public AjaxResult getPoolInfo(CoinVo vo) {
        //校验coin是否在规定范围之内
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("参数错误,未能根据当前参数获取到数据");
        }

        //构造返回参数

        PageHelper.clearPage();
        //todo 从30分钟表拿最新的一组 根据状态为online的统计 -> 从mhs_real表拿最新的一组 根据状态为online的统计->从mhs_realv2表拿 然后统一乘以算力系数
        //PoolInfoDto dto = poolMapper.getPoolInfoByCoin(pool.getMhs()+"_realv2");
        //if(StringUtils.isNull(dto)){
        //    dto = new PoolInfoDto();
        //}
        PoolInfoDto dto = new PoolInfoDto();

        if("enx".equals(pool.getCoin())){
            //enx 暂时不拿节点相关的东西(全网算力、高度、难度)
            dto.setTotalDifficulty("");
            dto.setTotalPower("");
            dto.setHeight(0);
            dto.setPrice("");
            Date now= new Date();
            if(now.before(DateUtils.parseDate("2025-04-21"))){
                dto.setFee(0);
            }
        }else {
            //从redis中拿币种当前高度、全网算力、全网难度
            BlockInfo info = redisService.getCacheObject(pool.getCoin() + "_block");
            if(StringUtils.isNotNull(info)){
//                //System.out.println("info:"+info);
//                //高度
//                dto.setHeight(info.getHeight());
//
//                //全网难度
//                dto.setTotalDifficulty(PowerUnitUtils.difficultyFormat(info.getDifficulty()));
//                Date now = new Date();
//                int minute = (now.getMinutes() / 30) * 30;
//                now.setMinutes( minute);
//                now.setSeconds(0);
//                Calendar ca = new GregorianCalendar();
//                ca.setTime(now);
//                ca.add(ca.DATE,-1);
//                String start = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, ca.getTime());
//                BigDecimal netPower = poolMapper.getAvgNetPower(pool.getCoin(), start);

                dto.setTotalDifficulty(PowerUnitUtils.difficultyFormat(info.getDifficulty()));

                //全网算力 调用转换工具 动态转换为数值+单位
                dto.setTotalPower(PowerUnitUtils.powerFormat(info.getPower(),pool.getCoin()));
                dto.setHeight(info.getHeight());
                //System.out.println("dto"+dto);
            }else {
                System.out.println("未能从节点获取到信息");
            }

            BigDecimal price = redisService.getCacheObject(pool.getCoin() + "_price");
            if (price != null){
                dto.setPrice(price.toEngineeringString()+ " USD");
            }
        }
        if("sha3x".equals(pool.getCoin())){
            dto.setFee(0.4);
        }


        //从enums中拿币种名、页面显示名、币种对应算法
        dto.setCoin(pool.getCoin());
        dto.setName(pool.getName());
        dto.setAlgorithm(pool.getAlgorithm());


        //从池子算力统计表pool_stats拿对应矿池算力  30m
        //从实时表拿24h
        double mhs = Convert.toDouble(poolMapper.getNowPoolDailyPower(pool.getMhs()+"_realv2"),0.0) * pool.getFac();

        PoolUnits poolUnit = (PoolUnits) EnumUtils.get(PoolUnits.class, vo.getCoin());
        //将数据库数据转换为H/s
        BigDecimal hPower = BigDecimal.valueOf(mhs).multiply(BigDecimal.valueOf(poolUnit.gethRate()));

        dto.setPoolPower(PowerUnitUtils.powerFormat(hPower,pool.getCoin()));


        //System.out.println("矿池详情数据"+dto);
        return AjaxResult.success(dto);
    }

    /**
     * 数据补零 30分钟时间端
     * @param dataList
     * @return
     */
    public static List<PowerLineDto> fillMissingOnlineData(List<PowerLineDto> dataList,Date startTime,Date endTime,String unit,int interval) {

        List<PowerLineDto> resultList = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();


        calendar.setTime(startTime);
        Map<Long, PowerLineDto> timeDataMap = new HashMap<>();
        for (PowerLineDto data : dataList) {
            timeDataMap.put(data.getDate().getTime(), data);
        }

        while (calendar.getTime().getTime() <= endTime.getTime()) {
            long currentTime = calendar.getTime().getTime();
            PowerLineDto data = timeDataMap.get(currentTime);
            if (data != null) {
                resultList.add(data);
            } else {
                if (calendar.getTime().getTime() != endTime.getTime()){
                    resultList.add(new PowerLineDto(calendar.getTime(),BigDecimal.valueOf(0), BigDecimal.valueOf(0),0,unit));
                }
            }
            // 增加 30 分钟
            calendar.add(Calendar.MINUTE, interval);
        }

        return resultList;
    }


    @Override
    public AjaxResult getPoolPower(CoinVo vo) {

        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("参数错误,未能根据当前参数获取到数据");
        }

        if(StringUtils.isBlank(vo.getInterval())){
            return AjaxResult.error("缺失参数:interval");
        }

        List<PowerLineDto> priceList = poolMapper.getHourPoolPowerList(pool.getCoin());

        int scale = PoolProfitScale.getScaleByCoin(pool.getCoin());
        PowerLineDto powerLineDto = priceList.stream()
                .filter(e -> e.getDate().after(DateUtils.toDate(LocalDateTime.now().toLocalDate().atStartOfDay())))
                .max(Comparator.comparing(PowerLineDto::getPrice)).orElse(new PowerLineDto());

        if("1h".equals(vo.getInterval()) || "rt".equals(vo.getInterval())){
            // 获取当前日期
            Date currentDate = DateUtils.getPreviousHalfHourOrFullHour(new Date());
            // 获取一天前的日期时间
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(currentDate);
            calendar.add(Calendar.DAY_OF_MONTH, -1);
            Date oneDayAgo = calendar.getTime();
            //30m
            PageHelper.clearPage();
            //矿池算力从$coin_pool_30m拿   获取的 算力单位为MH/S
            List<PowerLineDto> list = poolMapper.get30mPoolPowerList(pool.getPoolTable()+"_30m",oneDayAgo,currentDate);
            PowerUnitUtils.NetPowerUnit powerUnit;
            if (!list.isEmpty()) {
                //根据币种做参数处理
                PowerLineDto maxPv = list.stream().max(Comparator.comparing(PowerLineDto::getPv)).orElse(new PowerLineDto());
                powerUnit = PowerUnitUtils.getPowerUnit( maxPv.getPv().multiply(BigDecimal.valueOf(1000 * 1000)));
                list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1000 * 1000));
            }else{
                powerUnit = new PowerUnitUtils.NetPowerUnit("GH/S", BigDecimal.valueOf(1000 * 1000 * 1000));
            }
            //数据补零
            list = fillMissingOnlineData(list,oneDayAgo,currentDate,powerUnit.getUnit(),30);
            //通过全网算力接口 获取到的时间段价格补充到矿池算力接口的价格中
            list.stream().forEach(e -> priceList.stream().anyMatch(p ->{
                if( p.getDate().equals(e.getDate())){
                    e.setPrice(p.getPrice());
                }
                return false;
            }));

            List<PowerLineDto> collect = list.stream().peek(e -> {
                if (e.getPrice() == 0) {
                    e.setPrice(powerLineDto.getPrice());
                }
            }).collect(Collectors.toList());

            return AjaxResult.success(collect);
        }else if ("1d".equals(vo.getInterval())){

            //获取当天零点时间
            LocalDateTime todayStart = LocalDate.now().atStartOfDay();
            Date currentDate = Date.from(todayStart.atZone(ZoneId.systemDefault()).toInstant());
            // 获取一个月前的日期
            Date oneMonthAgo =  DateUtils.getOneMonthAgo(currentDate);
            PageHelper.clearPage();
            //矿池算力从$coin_pool_1d拿
            List<PowerLineDto> list = poolMapper.getDailyPoolPowerList(pool.getPoolTable()+"_24h",oneMonthAgo,currentDate);

            //根据币种做参数处理
            list.stream().forEach(e -> {
                e.setDate(DateUtils.addDays(e.getDate(),-1));
            });


            //从hashRate库中获取最后一天数据用作当天实时24h算力
            List<MinerDataDto> lastList = poolMapper.getMinerInfoList(pool.getMhs());
            BigDecimal mhs = lastList.stream().map(e -> {
                return BigDecimal.valueOf(e.getMhs24h());
            }).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(BigDecimal.valueOf(pool.getFac()));
            PowerLineDto lastPLDto= new PowerLineDto();
            Date now = DateUtils.parseDate(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD));
            lastPLDto.setDate(now);
            lastPLDto.setPv(mhs);
            list.add(lastPLDto);
            //根据集合中最大算力一个对象转换的后单位，把集合中所有元素都转换成该单位
            PowerLineDto maxPv = list.stream().max(Comparator.comparing(PowerLineDto::getPv)).orElse(new PowerLineDto());
            PowerUnitUtils.NetPowerUnit powerUnit = PowerUnitUtils.getPowerUnit(maxPv.getPv() .multiply(BigDecimal.valueOf(1000 * 1000)));
            list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1000 * 1000));

            list = fillMissingOnlineData(list,oneMonthAgo,currentDate,powerUnit.getUnit(),24*60);
            list.stream().forEach(e -> priceList.stream().anyMatch(p ->{
                if( p.getDate().equals(e.getDate())){
                    e.setPrice(p.getPrice());
                }
                return false;
            }));
            List<PowerLineDto> collect = list.stream().peek(e -> {
                if (e.getPrice() == 0) {
                    e.setPrice(powerLineDto.getPrice());
                }
            }).collect(Collectors.toList());
            return AjaxResult.success(collect);
        }else {
            return AjaxResult.error("参数错误:interval");
        }


    }

    @Override
    public boolean checkAccount(MinerAccountAddVo vo) {

        System.out.println("收到的参数为："+vo);

        //校验coin是否在规定范围之内
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return false;
        }

        if(uaMapper.checkAccountIfExist(vo.getMa(),vo.getCoin())){
            System.out.println(true);
            return true;
        }
        return false;
    }

    //@Override
    //public AjaxResult getMinerCount(CoinVo com.m2pool.chat.vo) {
    //
    //    Pools pool = (Pools) EnumUtils.get(Pools.class, com.m2pool.chat.vo.getCoin());
    //    if(StringUtils.isNull(pool)){
    //        return AjaxResult.error("参数错误,未能根据当前参数获取到数据");
    //    }
    //
    //    PageHelper.clearPage();
    //    List<MinerCountLineDto> list = poolMapper.getMinerCountList(pool.getPoolTable());
    //
    //    return AjaxResult.success(list);
    //}

    @Override
    public AjaxResult getLuckByCoin(CoinVo vo) {

        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("参数错误,未能根据当前参数获取到数据");
        }

        //幸运值
        //直接从redis拿 key ${coin}Luck ${coin}.toUP

        LuckDto dto = redisService.getCacheObject(pool.getCoin().toUpperCase()+"Luck");
        if(StringUtils.isNull(dto)){
            dto = new LuckDto();
        }

        return AjaxResult.success(dto);
    }

    @Override
    public List<BlockInfoDto> getBlockInfoList(CoinVo vo) {
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return null;
        }

        List<BlockInfoDto> list = new ArrayList<>();
        //todo 换到hashrate表查询新的矿池报块表
        switch (pool.getCoin()){
            case "grs": list=poolMapper.getGRSBlockInfoList();break;
            case "mona": list=poolMapper.getMonaBlockInfoList();break;
            case "dgbs": list=poolMapper.getDgbSkeinBlockInfoList();break;
            case "dgbq": list=poolMapper.getDgbQubitBlockInfoList();break;
            case "dgbo": list=poolMapper.getDGBODOBlockInfoList();break;
            case "dgb_qubit_a10": list=poolMapper.getDgbQubitA10BlockInfoList();break;
            case "dgb_skein_a10": list=poolMapper.getDgbSkeinA10BlockInfoList();break;
            case "dgb_odo_b20": list=poolMapper.getDGBODOB20BlockInfoList();break;
            case "nexa": list=poolMapper.getNEXABlockInfoList();break;
            case "rxd": list=poolMapper.getRXDBlockInfoList();break;
            case "alph": list=poolMapper.getALPHBlockInfoList();break;
            case "enx": list=poolMapper.getENXBlockInfoList();break;
            default:
                break;
        }

        //list.stream().forEach(e ->
        //        e.setReward(BigDecimal.valueOf(e.getReward()).subtract(BigDecimal.valueOf(e.getFees())).doubleValue()));
        return list;
    }

    //@Override
    //public AjaxResult getPoolPowerDistribution(CoinVo com.m2pool.chat.vo) {
    //    Pools pool = (Pools) EnumUtils.get(Pools.class, com.m2pool.chat.vo.getCoin());
    //
    //    if("rt".equals(com.m2pool.chat.vo.getInterval())){
    //        //实时分布 直接从miners_stats表拿最新数据
    //        //List<MinerDataDto> minerList= poolMapper.getNowMinerInfoList(pool.getMhs());
    //        //从30m算力表 mhs30m
    //        List<MinerDataDto> minerList= poolMapper.getMinerInfoList(pool.getMhs()+"30m");
    //
    //        List<DistributionDto> returnList = getDistributionDtoByDataList(minerList);
    //
    //        return AjaxResult.success(returnList);
    //    }
    //    else if ("1d".equals(com.m2pool.chat.vo.getInterval())){
    //        //从每日数据表拿最新一组数据 如 grs从grs_miners_1d表拿user为指定挖矿账户的一组数据
    //        String table = pool.getMhs()+"24h";
    //        List<MinerDataDto> list = poolMapper.getDailyPoolPowerDistributionList(table);
    //        List<DistributionDto> returnList = getDistributionDtoByDataList(list);
    //        return AjaxResult.success(returnList);
    //    }else {
    //        return AjaxResult.error("参数错误:interval");
    //    }
    //}

    @Override
    @Transactional(rollbackFor = Exception.class)
    @UserDB
    public AjaxResult getCoinBalanceList(BalanceListGetVo vo) {
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());

        if(StringUtils.isNull(pool)){
            return AjaxResult.error(707,"coin error");
        }

        List<BalanceListDto> list = uaMapper.getBalanceByCoin(pool.getCoin());
        return AjaxResult.success(list);
    }

    @Override
    public AjaxResult getNetPower(CoinVo vo) {
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("参数错误,未能根据当前参数获取到数据");
        }

        if(StringUtils.isBlank(vo.getInterval())){
            return AjaxResult.error("缺失参数:interval");
        }


        List<PowerLineDto> list = poolMapper.getHourNetPowerList(pool.getCoin());

        PowerLineDto powerLineDto = list.stream()
                .max(Comparator.comparing(PowerLineDto::getPrice)).orElse(new PowerLineDto());

         list = list.stream().peek(e -> {
            if (e.getPrice() == 0) {
                e.setPrice(powerLineDto.getPrice());
            }
        }).collect(Collectors.toList());


        if("1h".equals(vo.getInterval()) || "rt".equals(vo.getInterval())){
            PageHelper.clearPage();
            //30m数据 限制条数为最近1天的条数 即1*48=48
            list = list.stream().sorted(Comparator.comparing(PowerLineDto::getDate, Comparator.reverseOrder())).limit(48).collect(Collectors.toList());
            PowerLineDto maxPv = list.stream().max(Comparator.comparing(PowerLineDto::getPv)).orElse(new PowerLineDto());
            //if("alph".equals(pool.getCoin())){
            //    //alph 数据库按MH/s存的
            //    PowerUnitUtils.NetPowerUnit powerUnit = PowerUnitUtils.getPowerUnit(maxPv.getPv().multiply(BigDecimal.valueOf(1000)));
            //    list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1000));
            //}else{
            //
            //}
            PowerUnitUtils.NetPowerUnit powerUnit = PowerUnitUtils.getPowerUnit(maxPv.getPv());


            list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1));
            Collections.reverse(list);
            return AjaxResult.success(list);
        }else if ("1d".equals(vo.getInterval())){
            PageHelper.clearPage();
            //每天数据 从list中赛选Hour为0的值
            list = list.stream().filter(e -> e.getDate().getHours() == 0 && e.getDate().getMinutes() == 0 ).collect(Collectors.toList());
            if(list.size()>30){
                list = list.stream().sorted(Comparator.comparing(PowerLineDto::getDate, Comparator.reverseOrder())).limit(30).collect(Collectors.toList());
                Collections.reverse(list);
            }
            PowerLineDto maxPv = list.stream().max(Comparator.comparing(PowerLineDto::getPv)).orElse(new PowerLineDto());
            //if("alph".equals(pool.getCoin())){
            //    //alph 数据库按MH/s存的
            //    PowerUnitUtils.NetPowerUnit powerUnit = PowerUnitUtils.getPowerUnit(maxPv.getPv().multiply(BigDecimal.valueOf(1000)));
            //    list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1000));
            //}else{
            //
            //}
            PowerUnitUtils.NetPowerUnit powerUnit = PowerUnitUtils.getPowerUnit(maxPv.getPv());
            list = changeUnit(powerUnit, list, pool,BigDecimal.valueOf(1));
            return AjaxResult.success(list);
        }else {
            return AjaxResult.error("参数错误:interval");
        }
    }

    /**
     * 算力值单位换算
     * @param netPowerUnit  需要换算的单位 和值
     * @param list  需要换算的列表
     * @param pool 币种类型
     * @param factor 用于把初始power换算为H/S
     * @return
     */
    private List<PowerLineDto> changeUnit(PowerUnitUtils.NetPowerUnit netPowerUnit, List<PowerLineDto> list, Pools pool,BigDecimal factor) {
        int scale = PoolProfitScale.getScaleByCoin(pool.getCoin());
        list.forEach(e -> {
            if(StringUtils.isNotNull(e.getPv())){
                e.setUnit(netPowerUnit.getUnit());
                e.setPv(e.getPv().multiply(factor).divide(netPowerUnit.getUnitValue(),scale, RoundingMode.HALF_UP).stripTrailingZeros());
            }else {
                e.setUnit(netPowerUnit.getUnit());
                e.setPv(BigDecimal.valueOf(0));
            }
        });
        return list;
    }

    @Override
    public AjaxResult getParam(CoinVo vo) {
        Pools pool = (Pools) EnumUtils.get(Pools.class, vo.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error(606,"参数错误,未能获取到该币种数据");
        }

        CalParamDto dto = new CalParamDto();
        if("enx".equals(pool.getCoin())){
            dto.setNetHashrate(BigDecimal.valueOf(1));
            dto.setCount(BigDecimal.valueOf(86400));
            dto.setCoin(pool.getCoin());
            dto.setReward(BigDecimal.ZERO);
            Date now = new Date();
            if(now.before(DateUtils.parseDate("2025-04-21"))){
                dto.setPoolFee(BigDecimal.valueOf(0));
            }else {
                dto.setPoolFee(BigDecimal.valueOf(1));
            }

            PoolCalParamConfig calConfig = (PoolCalParamConfig) EnumUtils.get(PoolCalParamConfig.class, vo.getCoin());
            if(StringUtils.isNull(calConfig)){
                return AjaxResult.error(606,"参数错误,未能根据当前参数获取到数据");
            }

            dto.setCoin(pool.getCoin());
            dto.setCount(calConfig.getCount());


            //预计26-33 ph
            double hash = (Math.random() * 33) + 26;
            dto.setNetHashrate(BigDecimal.valueOf(hash).multiply(BigDecimal.valueOf(1000))
                            .multiply(BigDecimal.valueOf(1000))
                                    .multiply(BigDecimal.valueOf(1000))
                                            .multiply(BigDecimal.valueOf(1000))
                                                    .multiply(BigDecimal.valueOf(1000)).setScale(2,BigDecimal.ROUND_HALF_UP));
            dto.setPoolFee(calConfig.getPoolFees());

            dto.setReward(BigDecimal.valueOf(333));

            return AjaxResult.success(dto);

        }
        else {
            BlockInfo info = redisService.getCacheObject(pool.getCoin() + "_block");
            System.out.println("yyb-redis key"+ pool.getCoin() + "_block" +"查询结果"+ info);
            if(StringUtils.isNull(info)){
                if(!"enx".equals(pool.getCoin()) && !"alph".equals(pool.getCoin())){
                    //
                    return AjaxResult.error(606,"相关系数获取失败");
                }

            }

            PoolCalParamConfig calConfig = (PoolCalParamConfig) EnumUtils.get(PoolCalParamConfig.class, vo.getCoin());
            if(StringUtils.isNull(calConfig)){
                return AjaxResult.error(606,"参数错误,未能根据当前参数获取到数据");
            }



            dto.setCoin(pool.getCoin());
            dto.setCount(calConfig.getCount());
            dto.setNetHashrate(info.getPower());
            dto.setPoolFee(calConfig.getPoolFees());

            //BigDecimal price = redisService.getCacheObject(pool.getCoin() + "_price");
            //if(StringUtils.isNull(price)){
            //    return AjaxResult.error(606,"币价未获取到");
            //}
            //dto.setPrice(price);

            if(StringUtils.isNull(info.getReward())){
                return AjaxResult.error(606,"相关系数获取失败.");
            }

            if(StringUtils.isNull(info.getFees())){
                return AjaxResult.error(606,"相关系数获取失败。");
            }
            dto.setReward(info.getReward().subtract(info.getFees()));



            return AjaxResult.success(dto);
        }

    }

    @Override
    @DSTransactional
    public AjaxResult test2() {
        BlockInfo blockInfo = NodeRpc.getBlock("nexa");
        return AjaxResult.success(blockInfo);
    }

    @Override
    public AjaxResult test1() {
        String coin = "mona";
        String nowHeight = NodeRpc.getHeight(coin);

        if(nowHeight.contains("error:")){
            return AjaxResult.error("error");
        }

        BlockInfo blockInfo = new BlockInfo();
        //todo 调用getblockstats 拿subsidy/100000000做reward fees设置为0、拿blockhash
        Object[] statsParams = {Convert.toLong(nowHeight)};
        String blockstats = NodeRpc.getResultTest(coin, "getblockstats", statsParams);
        if(StringUtils.isBlank(blockstats)){
            System.out.println("blockstats查询结果为空");
            return AjaxResult.success(blockInfo);
        }

        if(blockstats.contains("error:")){
            System.out.println("blockstats包含错误："+blockstats);
            return AjaxResult.success(blockInfo);
        }

        JSONObject statsJs = JSON.parseObject(blockstats);
        String blockhash = statsJs.getString("blockhash");
        if(StringUtils.isBlank(blockstats)){
            System.out.println("blockhash获取结果为空");
            return AjaxResult.success(blockInfo);
        }
        System.out.println(blockhash);


        BigDecimal subsidy = statsJs.getBigDecimal("subsidy");
        if(StringUtils.isNull(subsidy)) {
            System.out.println("subsidy获取结果为空");
            return AjaxResult.success(blockInfo);

        }
        subsidy = subsidy.divide(BigDecimal.valueOf(Math.pow(10,8)),4,BigDecimal.ROUND_HALF_UP);
        System.out.println(subsidy);
        Object[] params = {blockhash};

        String result = NodeRpc.getResultTest(coin, "getblockheader", params);
        System.out.println("result"+result);



        if(StringUtils.isBlank(result)){
            System.out.println("查询结果为空");
            return AjaxResult.success(blockInfo);

        }

        if(result.contains("error:")){
            System.out.println("包含错误："+result);
            return AjaxResult.success(blockInfo);
        }

        JSONObject jsonObject = JSON.parseObject(result);
        long height = jsonObject.getLongValue("height");
        blockInfo.setHeight(height);

        BigDecimal difficulty = jsonObject.getBigDecimal("difficulty");
        if(StringUtils.isNotNull(difficulty)){
            blockInfo.setDifficulty(difficulty.setScale(2,BigDecimal.ROUND_HALF_UP));
            NodeConstant constant = (NodeConstant) EnumUtils.get(NodeConstant.class, coin);
            BigDecimal factor =  constant.getFactor();
            if(StringUtils.isNotNull(factor)){
                blockInfo.setPower(difficulty.multiply(factor).setScale(2,BigDecimal.ROUND_HALF_UP));
            }

        }

        blockInfo.setReward(subsidy);
        blockInfo.setFees(BigDecimal.valueOf(0));
        blockInfo.setProfit(blockInfo.getReward().subtract(blockInfo.getFees()));
        return AjaxResult.success(blockInfo);
    }


    public List<DistributionDto> getDistributionDtoByDataList(List<MinerDataDto> list){

        Map<Integer,Integer> map =new LinkedHashMap<>();
        map.put(0,0);
        map.put(5,0);
        map.put(10,0);
        map.put(15,0);
        map.put(20,0);
        map.put(25,0);
        map.put(30,0);
        map.put(35,0);
        map.put(40,0);
        map.put(45,0);
        map.put(50,0);
        map.put(55,0);
        map.put(60,0);
        map.put(65,0);
        map.put(70,0);
        map.put(75,0);
        map.put(80,0);
        map.put(85,0);
        map.put(90,0);
        map.put(95,0);
        list.stream().forEach(e -> {
            int a = (int) ((Math.round(e.getMhs()) / 1000) / 5 * 5);
            if(map.containsKey(a)){
                map.replace(a,map.get(a)+1);
            }else {
                map.put(a,1);
            }
        });
        List<DistributionDto> returnList = map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey()))
                .map(e -> {
                    DistributionDto  dto = new DistributionDto();
                    dto.setLow(e.getKey());
                    dto.setHigh(e.getKey()+5);
                    dto.setCount(e.getValue());
                    return dto;
                }).collect(Collectors.toList());

        return returnList;
    }


    @Override
    public AjaxResult getMinerPowerFor30min(MinerMhsVo minerMhsVo) {
        if (StringUtils.isNull(minerMhsVo.getCoin()) || StringUtils.isNull(minerMhsVo.getStartTime()) || StringUtils.isNull(minerMhsVo.getEndTime())){
            return AjaxResult.error("币种或开始日期或结束日期参数未传递");
        }
        List<MinerMhsDto> minerPowerFor30min = poolMapper.getMinerPowerFor30min(minerMhsVo.getCoin(), minerMhsVo.getStartTime(), minerMhsVo.getEndTime());

        return AjaxResult.success(minerPowerFor30min);
    }

    @Override
    @DSTransactional
    public String dataSupplement(DataSupplementVo vo) {

        String nowStr = vo.getDate();

        //检查二级表是否有当前时间数据
        //int count = poolMapper.getLastDataTime(vo.getCoin() + "_mhs30m", nowStr);


            List<MinerDataDto> list = poolMapper.getHourMinerDataList(vo.getCoin()+"_mhsv2",nowStr);
            if(list.size() > 0){
                List<MinerDataDto> offlineList = new ArrayList<>();
                list.stream().forEach(e -> {
                    if(StringUtils.isNotNull(e.getMhs())){
                        e.setMhs(e.getMhs() * Pools.NEXA.getFac());
                    }
                    if("offline".equals(e.getState())){
                        offlineList.add(e);
                    }
                });
                //list = DataTask.filterList(list,now);

                boolean result = poolMapper.batchInsertMhsDataToDB(vo.getCoin() + "_mhs30m", DataTask.getToHourDBListByMinerDataList(list));

                Map<Date, List<MinerDataDto>> map = list.stream().collect(Collectors.groupingBy(MinerDataDto::getDate));
                map.forEach((date,dList) -> {
                    PoolPower poolPower  = new PoolPower();
                    //poolPower.setDate(date);
                    poolPower.setDateStr(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,date));
                    Map<String, Long> stateCount = dList.stream().collect(Collectors.groupingBy(MinerDataDto::getState, Collectors.counting()));
                    poolPower.setMhs(dList.stream().map(e -> BigDecimal.valueOf(e.getMhs())).reduce(BigDecimal::add).get().stripTrailingZeros());
                    poolPower.setMiners(dList.size());
                    poolPower.setOnline(Convert.toInt(stateCount.get("online"),0));
                    poolPower.setOffline(Convert.toInt(stateCount.get("offline"),0));

                    boolean poolResult = poolMapper.insertPoolPower(vo.getCoin() + "_pool_30m", poolPower);
                    int time =0;
                    while (!poolResult){
                        poolResult = poolMapper.insertPoolPower(vo.getCoin() + "_pool_30m", poolPower);
                        if (time > 5) {
                            break;
                        }
                        time ++;
                    }

                    System.out.println("构造写入user算力表数据");
                    Map<String, List<MinerDataDto>> userMap = dList.stream().collect(Collectors.groupingBy(MinerDataDto::getUser));
                    List<MinerDataInsertDBDto> userList = new ArrayList<>();
                    userMap.forEach((user,uList)->{
                        MinerDataInsertDBDto uDto = new MinerDataInsertDBDto();
                        uDto.setDate(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,date));
                        uDto.setUser(user);
                        uDto.setMhs(uList.stream().map(e->BigDecimal.valueOf(e.getMhs())).reduce(BigDecimal::add).get().doubleValue());
                        userList.add(uDto);
                    } );
                    System.out.println("user算力表入库数据条数："+userList.size());
                    boolean userResult = poolMapper.batchInsertUserMhsDateToDB(vo.getCoin() + "_users_30m", userList);
                    int uTime =0;
                    while (!userResult){
                        userResult = poolMapper.batchInsertUserMhsDateToDB(vo.getCoin() + "_users_30m", userList);
                        if (uTime > 5) {
                            break;
                        }
                        uTime ++;
                    }




                });

                Map<String, List<MinerDataDto>> userMap = list.stream().collect(Collectors.groupingBy(MinerDataDto::getMiner));
                if (result) {
                    System.out.println(DateUtils.dateTimeNow() + "nexa小时数据存入数据成功");
                } else {
                    System.out.println(DateUtils.dateTimeNow() + "nexa小时数据存入数据失败");
                }

                if(offlineList.size() >0){
                    CompletableFuture.runAsync(()->{

                        //根据挖矿账户分组、统计离线矿机名单、离线矿机数

                        //对比redis中该挖矿账户上一次离线矿机数
                        //有redis数据 需要判断 上一次离线矿机数<本次矿机离线数 才执行通知 否则跳过
                        //无redis数据 说明是第一次 执行通知
                    });

                }
                return "成功";

            }


        return "hashrate库"+vo.getCoin()+"_mhsv2表未查到数据,补充数据失败";
    }


    @Override
    public String dataDelete(DataSupplementVo vo) {
        int i = poolMapper.deleteUsers30m(vo.getCoin(), vo.getDate());
        int i1 = poolMapper.deletePool30m(vo.getCoin(), vo.getDate());
        int i2 = poolMapper.deleteMhs30m(vo.getCoin(), vo.getDate());
        return "删除"+vo.getCoin()+"_mhs30m表"+i+"条数据，删除"+vo.getCoin()+"_pool_30m表"+i1+"条数据，删除"+vo.getCoin()+"_users_30m表"+i2+"条数据";
    }
}
