package com.m2pool.lease.service.impl;

import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.constant.BlockInterval;
import com.m2pool.lease.redis.RedisCoinInfoKey;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.entity.LeaseProduct;
import com.m2pool.lease.entity.LeaseProductMachine;
import com.m2pool.lease.entity.LeaseProductMachinePrice;
import com.m2pool.lease.mapper.LeaseProductMachineMapper;
import com.m2pool.lease.mapper.LeaseProductMapper;
import com.m2pool.lease.service.LeaseProductMachinePriceService;
import com.m2pool.lease.service.LeaseProductMachineService;
import com.m2pool.lease.utils.PowerUnitUtils;
import com.m2pool.lease.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品表对应的物品机器表 服务实现类
 * </p>
 *
 * @author yyb
 * @since 2025-07-23
 */
@Service
public class LeaseProductMachineServiceImpl extends ServiceImpl<LeaseProductMachineMapper, LeaseProductMachine> implements LeaseProductMachineService {

    @Resource
    private LeaseProductMachineMapper leaseProductMachineMapper;
    @Resource
    private LeaseProductMapper leaseProductMapper;
    @Resource
    private RedisService redisService;
    @Resource
    private LeaseProductMachinePriceService leaseProductMachinePriceService;
    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Result<Map<String, List<UserMinerDto>> > getUserMinersList(UserMinerVo userMinerVo) {
        String userId = SecurityUtils.getUsername();

        //开发环境
        //userId = "Eudora.law@outlook.com";
        List<UserMinerDto> userMinersList = leaseProductMachineMapper.getUserMinersList(userId,userMinerVo.getCoin());
        Map<String, List<UserMinerDto>> collect = userMinersList.stream().collect(Collectors.groupingBy(UserMinerDto::getCoin));
        return Result.success(collect);
    }

    @Override
    public Result<List<UserMinerDto>> getUserMachineList(UserMinerVo userMinerVo) {
        //获取1天内的存在算力的机器（这里user 和coin 组成了表的唯一键，不用担心查询到其他用户相同的user和coin的矿机）
        List<UserMinerDto> minerMachines = leaseProductMachineMapper.getMinerMachines(userMinerVo.getUser(), userMinerVo.getCoin());
        //根据矿工账号查询已经新增到商品机器表中的机器
        List<String> minerMachinesIsSale = leaseProductMachineMapper.getMinerMachinesIsSale(userMinerVo.getUser(), userMinerVo.getCoin());
        // 过滤 minerMachines 集合，只保留 miner 字段值不在 existingMiners 中的元素
        minerMachines = minerMachines.stream()
                .filter(dto -> !minerMachinesIsSale.contains(dto.getMiner()))
                .peek(dto -> {
                    dto.setRealPower(dto.getRealPower().divide(BigDecimal.valueOf(24 * 60 * 60),2, RoundingMode.HALF_UP));
                    dto.setRealPower(dto.getRealPower().setScale(2, RoundingMode.HALF_UP));
                })
                .collect(Collectors.toList());
        return Result.success(minerMachines);
    }

    @Override
    public PageResult<ProductUpdateMachineDto> getMachineListForUpdate(ProductForUpdateMachineVo productForUpdateMachineVo) {
        PageHelper.startPage(productForUpdateMachineVo.getPageNum(), productForUpdateMachineVo.getPageSize());
        List<LeaseProductMachine> leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper<LeaseProductMachine>()
                .eq(LeaseProductMachine::getProductId, productForUpdateMachineVo.getId()).eq(LeaseProductMachine::getDel,false));
        PageInfo<LeaseProductMachine> pageInfo = new PageInfo<>(leaseProductMachines);
        if (leaseProductMachines.isEmpty()){
            return PageResult.fail(new ArrayList<>(), "不存在租售的矿机");
        }
        //获取矿机对应的币价
        List<Long> machineIds = leaseProductMachines.stream().map(LeaseProductMachine::getId).collect(Collectors.toList());
        List<MachinePayTypeDto> priceList = leaseProductMachineMapper.getPriceList(machineIds);
        Map<Long, List<MachinePayTypeDto>> collect = priceList.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));

        List<ProductUpdateMachineDto> productUpdateMachineDtos = leaseProductMachines.stream().map(leaseProductMachine ->
             ProductUpdateMachineDto.builder()
                    .id(leaseProductMachine.getId())
                    .user(leaseProductMachine.getUser())
                    .type(leaseProductMachine.getType())
                    .miner(leaseProductMachine.getMiner())
                    .priceList(collect.get(leaseProductMachine.getId()))
                    .computingPower(leaseProductMachine.getComputingPower().setScale(2, RoundingMode.HALF_UP))
                    .theoryPower(leaseProductMachine.getTheoryPower())
                    .unit(leaseProductMachine.getUnit())
                     .maxLeaseDays(leaseProductMachine.getMaxLeaseDays())
                     .state(leaseProductMachine.getState())
                      .saleState(leaseProductMachine.getSaleState())
                     .powerDissipation(leaseProductMachine.getPowerDissipation())
                     .build()
        ).collect(Collectors.toList());
        PageResult<ProductUpdateMachineDto> success = PageResult.success(productUpdateMachineDtos);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    @DSTransactional
    public Result<String> addSingleOrBatchMachine(ProductMachineParamsVo productMachineParamsVo) {
        LeaseProduct product = leaseProductMapper.selectById(productMachineParamsVo.getProductId());

        String coin = product.getCoin();
        if (coin == null){
            return Result.fail("商品币种不能为空");
        }
        //封装需要添加的商品的机器
        List<LeaseProductMachine> leaseProductMachines = new ArrayList<>();
        List<ProductMachineURDVo> productMachineURDVos = productMachineParamsVo.getProductMachineURDVos();

        Map<Integer,List<LeaseProductMachinePrice>> payTypeMap = new HashMap<>();
        AtomicInteger index = new AtomicInteger(1);
        productMachineURDVos.forEach(machine -> {
            int andIncrement = index.getAndIncrement();
            leaseProductMachines.add(LeaseProductMachine.builder()
                        .updateCount(andIncrement)
                        .shopId(product.getShopId())
                        .productId(productMachineParamsVo.getProductId())
                        .type(machine.getType() == null ? productMachineParamsVo.getType() : machine.getType())
                        .theoryPower(machine.getTheoryPower() == null ? productMachineParamsVo.getTheoryPower() : machine.getTheoryPower())
                        .powerDissipation(productMachineParamsVo.getPowerDissipation())
                        .cost(productMachineParamsVo.getCost())
                        .price(BigDecimal.ZERO)
                        .incomeRate(BigDecimal.ZERO)
                        .maxLeaseDays(machine.getMaxLeaseDays() == null ? productMachineParamsVo.getMaxLeaseDays() : machine.getMaxLeaseDays())
                        .unit(machine.getUnit() == null ? productMachineParamsVo.getUnit() : machine.getUnit())
                        .powerDissipation(machine.getPowerDissipation() == null ? productMachineParamsVo.getPowerDissipation() : machine.getPowerDissipation())
                        .user(machine.getUser())
                        .miner(machine.getMiner())
                        .coin(coin)
                        .state(machine.getState())
                        .del( false)
                        .saleState(0)
                        .build());
            payTypeMap.put(andIncrement,machine.getPriceList().stream()
                    .map(payType -> LeaseProductMachinePrice.builder()
                            .price(payType.getPrice())
                            .coin(payType.getCoin())
                            .chain(payType.getChain())
                            .build())
                    .collect(Collectors.toList()));

        });



        //计算对应user+miner 一天的总算力
        List<ProductMachineDto> nexaMinersv2 = leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, product.getCoin());
        // 创建一个 Map 用于存储 user 和 miner 组合对应的 ProductMachineDto 对象
        Map<String, ProductMachineDto> machineDtoMap = nexaMinersv2.stream()
                .collect(Collectors.toMap(
                        dto -> dto.getUser() + "-" + dto.getMiner(),
                        dto -> dto
                ));
        String priceKey = RedisCoinInfoKey.getPiceKey(product.getCoin());
        String rewardKey = RedisCoinInfoKey.getRewardKey(product.getCoin());
        String mhsKey = RedisCoinInfoKey.getMhsKey(product.getCoin());

        if (priceKey == null || rewardKey == null || mhsKey == null){
            return Result.fail("添加商品失败,该币种挖矿机器暂时不支持");
        }

        BigDecimal price = redisService.getCacheBigDecimal(priceKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(priceKey);
        BigDecimal reward = redisService.getCacheBigDecimal(rewardKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(rewardKey);
        //全网算力单位是MH/s
        BigDecimal netPower = redisService.getCacheBigDecimal(mhsKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(mhsKey);

        if (price == null){
            return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的币价，请稍后重试");
        }
        if (reward == null){
            return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的报快奖励，请稍后重试");
        }
        if (netPower == null){
            return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的全网算力，请稍后重试");
        }



        List<LeaseProductMachine> collect = leaseProductMachines.stream().peek(productMachineURDVo -> {
            //获取单机实际算力
            String key = productMachineURDVo.getUser() + "-" + productMachineURDVo.getMiner();
            ProductMachineDto productMachineDto = machineDtoMap.get(key);
            BigDecimal singleTheoryMachineIncome = BigDecimal.ZERO;
            BigDecimal computingPower = BigDecimal.ZERO;

            if (productMachineDto != null && productMachineDto.getComputingPower() != null){
                //计算矿机一天内实际每秒平均算力
                 computingPower = PowerUnitUtils.getPower(productMachineURDVo.getUnit(), productMachineDto.getComputingPower()
                        .multiply(BigDecimal.valueOf(1000 * 1000))
                        .divide(BigDecimal.valueOf(24 * 60 * 60), 2, RoundingMode.HALF_UP));
                BigDecimal power = PowerUnitUtils.getPower(productMachineURDVo.getUnit(), netPower.multiply(BigDecimal.valueOf(1000 * 1000)));
                //理论收益 = 矿机算力/全网算力 * 24h /出块间隔秒数 * 单块奖励（redis中nexa:reward）
                singleTheoryMachineIncome = computingPower.divide( power,6, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(24 * 60 * 60)
                        .divide(BlockInterval.getBlockCountByCoinName(product.getCoin()), 6, RoundingMode.HALF_UP))
                        .multiply(reward)
                ;

            }
            productMachineURDVo.setShopId(product.getShopId());
            productMachineURDVo.setComputingPower(computingPower);
            productMachineURDVo.setTheoryIncome(singleTheoryMachineIncome);
        }).collect(Collectors.toList());
        //新增矿机 索引改为普通联合索引
        LeaseProductMachineServiceImpl service = applicationContext.getBean(LeaseProductMachineServiceImpl.class);
        //TODO 修改,还是得用唯一索引
        //leaseProductMachineMapper.saveOrUpdateBatchs(collect);
        boolean b = service.saveBatch(collect);
        if (b){
            //修改商品的价格范围 及商品下机器库存数量
            updatePriceRange(product.getId());
            List<LeaseProductMachinePrice> list = new ArrayList<>();
            for (LeaseProductMachine leaseProductMachine : collect) {

                List<LeaseProductMachinePrice> leaseProductMachinePrices = payTypeMap.get(leaseProductMachine.getUpdateCount());
                list.addAll(leaseProductMachinePrices.stream()
                        .peek(leaseProductMachinePrice -> leaseProductMachinePrice.setProductMachineId(leaseProductMachine.getId()))
                        .collect(Collectors.toList()));
            }
            //修改机器配置
            leaseProductMachinePriceService.saveBatch(list);
            return Result.success("添加成功,添加矿机数："+collect.size());
        }
        return Result.fail("添加失败");
    }

    @Override
    @Transactional
    public Result<String> updateMachine(List<ProductUpdateMachineVo> productUpdateMachineVoList) {

        List<Long> ids = new ArrayList<>();
        List<LeaseProductMachine> leaseProductMachines = new ArrayList<>();
        List<LeaseProductMachinePrice> list = new ArrayList<>();
        productUpdateMachineVoList.forEach(machine -> {
            leaseProductMachines.add(        LeaseProductMachine.builder()
                     .id(machine.getId())
                    .type(machine.getType())
                    .theoryPower(machine.getTheoryPower())
                    .unit(machine.getUnit())
                    .powerDissipation(machine.getPowerDissipation())
                    //.electricityBill(machine.getElectricityBill())
                    .incomeRate(machine.getIncomeRate())
                    .price(machine.getPrice())
                    .maxLeaseDays(machine.getMaxLeaseDays())
                    .user(machine.getUser())
                    .miner(machine.getMiner())
                    .state(machine.getState())
                    .build());
            list.addAll(machine.getPriceList().stream()
                    .map(payType -> LeaseProductMachinePrice.builder()
                            .id(payType.getPayTypeId())
                            .price(payType.getPrice())
                            .chain(payType.getChain())
                            .build())
                    .collect(Collectors.toList()));
            if (machine.getState() == 1){
                ids.add(machine.getId());
            }
        });
        if (!ids.isEmpty()){
            List<LeaseProductMachine> leaseProductMachines1 = leaseProductMachineMapper.selectBatchIds(ids);
            long count = leaseProductMachines1.stream()
                    .filter(leaseProductMachine -> leaseProductMachine.getSaleState() == 1).count();
            if (count >= 1){
                return Result.fail("商品修改失败,部分矿机租约已存在,不能下架");
            }
        }



        boolean b = updateBatchById(leaseProductMachines);
        if (b){
            LeaseProductMachine leaseProductMachine = leaseProductMachineMapper.selectById(productUpdateMachineVoList.get(0).getId());
            //修改商品的价格范围
            updatePriceRange(leaseProductMachine.getProductId());
            leaseProductMachinePriceService.updateBatchById(list);
            return Result.success("修改成功");
        }
        return Result.fail("修改失败");
    }


    @Override
    @Transactional
    public Result<String> deleteMachine(BaseVo baseVo) {
        LeaseProductMachine leaseProductMachine = leaseProductMachineMapper.selectById(baseVo.getId());
        if (leaseProductMachine.getSaleState() != 0){
            return Result.fail("该商品已售出，暂时不能删除");
        }
        //修改商品的价格范围 及商品下机器库存数量

        boolean b = updateById(LeaseProductMachine.builder().id(baseVo.getId()).del(true).build());
        if (b){
            updatePriceRange(leaseProductMachine.getProductId());
            //删除售价
            leaseProductMachinePriceService.updateById(LeaseProductMachinePrice.builder().id(baseVo.getId()).del(true).build());
            return Result.success("删除成功");
        }
        return Result.fail("删除失败");
    }

    /**
     * 修改商品价格范围
     * @param productId
     */
    public void updatePriceRange(Long productId){

        //修改商品的价格范围
        PriceDto priceDto = leaseProductMachineMapper.getPriceRange(productId);
        BigDecimal maxPrice = BigDecimal.ZERO;
        BigDecimal minPrice = BigDecimal.ZERO;
        Integer totalMachineNumber = 0;
        if (priceDto != null){
             totalMachineNumber = priceDto.getTotalMachineNumber();
             maxPrice = priceDto.getPriceMax();
             minPrice = priceDto.getPriceMin();
        }
        LeaseProduct build = LeaseProduct.builder().id(productId)
                .minPrice(minPrice)
                .maxPrice(maxPrice)
                .totalMachineNumber(totalMachineNumber )
                .build();
        leaseProductMapper.updateById(build);
    }

    @Override
    public Result<BigDecimal> calculatePrice(PriceCalculateVo priceCalculateVo) {
        //TODO 第三方计算器

        return null;
    }
}
