package com.ruoyi.ss.device.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.channelWithdraw.domain.ChannelWithdrawQuery;
import com.ruoyi.ss.channelWithdraw.domain.ChannelWithdrawVO;
import com.ruoyi.ss.channelWithdraw.service.ChannelWithdrawService;
import com.ruoyi.ss.device.domain.Device;
import com.ruoyi.ss.device.domain.DeviceBO;
import com.ruoyi.ss.device.domain.DeviceCountVO;
import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceBatchUpdateModelDTO;
import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.dto.DeviceWifiDTO;
import com.ruoyi.ss.device.domain.dto.PlacementDTO;
import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.enums.DeviceStatus;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.mapper.DeviceMapper;
import com.ruoyi.ss.device.service.DeviceAssembler;
import com.ruoyi.ss.device.service.DeviceConverter;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.device.service.DeviceValidator;
import com.ruoyi.ss.deviceBindRecord.domain.enums.BindRecordType;
import com.ruoyi.ss.deviceBindRecord.domain.enums.BindRecordUserType;
import com.ruoyi.ss.deviceBindRecord.service.DeviceBindRecordService;
import com.ruoyi.ss.equipment.domain.Equipment;
import com.ruoyi.ss.equipment.domain.EquipmentVO;
import com.ruoyi.ss.equipment.service.IEquipmentService;
import com.ruoyi.ss.model.domain.ModelVO;
import com.ruoyi.ss.model.domain.enums.ModelTag;
import com.ruoyi.ss.model.service.IModelService;
import com.ruoyi.ss.order.domain.OrderVO;
import com.ruoyi.ss.order.service.IOrderValidator;
import com.ruoyi.ss.room.domain.RoomVO;
import com.ruoyi.ss.room.service.IRoomService;
import com.ruoyi.ss.store.domain.Store;
import com.ruoyi.ss.store.domain.StoreVO;
import com.ruoyi.ss.store.service.IStoreService;
import com.ruoyi.ss.toilet.domain.Toilet;
import com.ruoyi.ss.toilet.domain.ToiletVO;
import com.ruoyi.ss.toilet.service.IToiletService;
import com.ruoyi.ss.user.domain.UserVO;
import com.ruoyi.ss.user.service.IUserService;
import com.ruoyi.ss.userWithdraw.domain.UserWithdraw;
import com.ruoyi.ss.userWithdraw.service.IUserWithdrawService;
import com.ruoyi.system.iot.domain.IotDeviceInfo;
import com.ruoyi.system.iot.domain.response.CommandResponse;
import com.ruoyi.system.iot.enums.IotHttpStatus;
import com.ruoyi.system.iot.service.IotService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.ruoyi.common.constant.ServiceConstants.*;
import static com.ruoyi.common.utils.SecurityUtils.getUserId;

/**
 * 设备Service业务层处理
 *
 * @author 邱贞招
 * @date 2024-02-21
 */
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IotService iotService;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private DeviceAssembler deviceAssembler;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private DeviceConverter deviceConverter;

    @Autowired
    private DeviceValidator deviceValidator;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private IRoomService roomService;

    @Autowired
    private IUserService userService;

    @Autowired
    private IOrderValidator orderValidator;

    @Autowired
    private IModelService modelService;

    @Autowired
    private IEquipmentService equipmentService;

    @Resource
    private IToiletService toiletService;

    @Autowired
    private IStoreService storeService;

    @Autowired
    private DeviceBindRecordService deviceBindRecordService;

    @Autowired
    private IUserWithdrawService userWithdrawService;

    @Autowired
    private ChannelWithdrawService channelWithdrawService;

    /**
     * 查询设备
     *
     * @param deviceId 设备主键
     * @return 设备
     */
    @Override
    public DeviceVO selectById(Long deviceId)
    {
        return deviceMapper.selectSmDeviceByDeviceId(deviceId);
    }

    /**
     * 查询设备列表
     *
     * @param smDevice 设备
     * @return 设备
     */
    @Override
    public List<DeviceVO> selectSmDeviceList(DeviceQuery smDevice) {
        if(smDevice.getRoomId() != null){
            List<Long> longs = deviceMapper.selectIdsByRoomId(smDevice.getRoomId());
            // 如果longs是空的话传一个Long值为1的长度为1的数组
            if(longs.isEmpty()){
                smDevice.setDeviceIds(Collections.singletonList(0L));
            }else {
                smDevice.setDeviceIds(longs);
            }
        }
        return deviceMapper.selectSmDeviceList(smDevice);
    }

    /**
     * 新增设备
     * @param data 设备
     * @return 结果
     */
    @Override
    public int insertSmDevice(Device data) {
        ServiceUtil.assertion(data == null, "参数错误，不允许为空");
        ServiceUtil.assertion(StringUtils.isBlank(data.getSn()), "参数错误，SN不允许为空");
        ServiceUtil.assertion(StringUtils.isBlank(data.getMac()), "参数错误，MAC不允许为空");

        String key = data.getSn();
        ServiceUtil.assertion(!redisLock.lock(RedisLockKey.ADD_DEVICE, key), "当前新增设备过于频繁，请稍后再试");
        try {

            ModelVO model = modelService.selectSmModelByModelId(data.getModelId());
            ServiceUtil.assertion(model == null, "型号不存在");
            ServiceUtil.assertion(StringUtils.isBlank(model.getProductId()), "型号产品ID未配置，请联系管理员处理");

            data.setCreateTime(DateUtils.getNowDate());
            data.setStatus(DeviceStatus.NORMAL.getStatus());
            data.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());

            Integer result = transactionTemplate.execute(status -> {
                // 创建设备
                int insert = deviceMapper.insertSmDevice(data);
                ServiceUtil.assertion(insert != 1, "新增设备失败");

                // 后校验
//                DeviceVO vo = this.selectById(data.getDeviceId());
//                deviceValidator.afterCheck(vo);

                //创建OneNet设备1
                int code = iotService.create(data.getMac(), model.getProductId());
                ServiceUtil.assertion(!IotHttpStatus.SUCCESS.equalCode(code) && !IotHttpStatus.DEVICE_EXIST.equalCode(code), "MAC-1注册失败");
                // 创建OneNet设备2
                if (StringUtils.hasText(data.getMac2())) {
                    code = iotService.create(data.getMac2(), model.getProductId());
                    ServiceUtil.assertion(!IotHttpStatus.SUCCESS.equalCode(code) && !IotHttpStatus.DEVICE_EXIST.equalCode(code), "MAC-2注册失败");
                }

                return insert;
            });
            return result == null ? 0 : result;
        } finally {
            redisLock.unlock(RedisLockKey.ADD_DEVICE, key);
        }
    }

    /**
     * 修改设备
     *
     * @param data 设备
     * @return 结果
     */
    @Override
    public int updateSmDevice(Device data) {

        data.setUpdateTime(DateUtils.getNowDate());

        // 修改数据库
        Integer result = transactionTemplate.execute(status -> {
            int update = deviceMapper.updateSmDevice(data);
            ServiceUtil.assertion(update != 1, "更新设备失败");
            return update;
        });

        return result == null ? 0 : result;
    }

    /**
     * 批量删除设备
     *
     * @param deviceIds 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteSmDeviceByDeviceIds(Long[] deviceIds)
    {
        this.validatePreDelete(Arrays.asList(deviceIds));
        return deviceMapper.deleteSmDeviceByDeviceIds(deviceIds);
    }

    /**
     * 真正的删除校验
     */
    private void validatePreDelete(List<Long> deviceIds) {
        ServiceUtil.assertion(CollectionUtils.isEmpty(deviceIds), "设备不能为空");
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        dto.setDeleted(true);
        Map<Long, DeviceVO> deviceMap = selectMap(dto);

        for (Long deviceId : deviceIds) {
            DeviceVO device = deviceMap.get(deviceId);
            ServiceUtil.assertion(device == null, String.format("设备“%s”不存在，请刷新后重试", deviceId));
        }
    }

    /**
     * 逻辑删除校验
     */
    private void validatePreLogicDelete(List<Long> deviceIds) {
        ServiceUtil.assertion(CollectionUtils.isEmpty(deviceIds), "设备不能为空");

        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        dto.setDeleted(false);
        // 前台用户，判断是不是他的设备
//        if (LoginType.FRONT.equals(SecurityUtils.getLoginType())) {
//            dto.setUserId(SecurityUtils.getUserId());
//        }
        Map<Long, DeviceVO> deviceMap = selectMap(dto);

        for (Long deviceId : deviceIds) {
            DeviceVO device = deviceMap.get(deviceId);
            ServiceUtil.assertion(device == null, String.format("设备“%s”不存在，请刷新后重试", deviceId));
        }

    }

    /**
     * 条件查询列表，并转为Map
     */
    private Map<Long, DeviceVO> selectMap(DeviceQuery dto) {
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(DeviceVO::getDeviceId, item -> item));
    }

    @Override
    public int selectCount(DeviceQuery dto) {
        return deviceMapper.selectCount(dto);
    }

    @Override
    public List<DeviceVO> selectSmDeviceByStoreId(Long storeId) {
        DeviceQuery dto = new DeviceQuery();
        dto.setStoreId(storeId);
        dto.setDeleted(false);
        return deviceMapper.selectSmDeviceList(dto);
    }


    @Override
    public boolean addTimeByUser(Long deviceId, long seconds, boolean withIot, String reason) {
        boolean addTime = this.addTime(deviceId, seconds, withIot);

//        if (addTime) {
//            // 记录下操作日志
//            LoginUser loginUser = SecurityUtils.getLoginUser();
//            scheduledExecutorService.schedule(() -> {
//                DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
//                recordTimeService.insertRecordTime(recordTimeConverter.toRecordTime(device, seconds, reason, loginUser, RecordTimeType.TIME.getType()));
//            }, 0, TimeUnit.SECONDS);
//        }

        return addTime;
    }

    @Override
    public int register(DeviceRegisterDTO dto) {
        if (dto == null) {
            return 0;
        }

        DeviceBO device = deviceConverter.toBOByRegister(dto);
        DeviceVO old = selectByAnyMac(dto.getMac());

        if (old == null) {
            // 若设备MAC不存在，则新增
            return this.insertSmDevice(device);
        } else {
            // 若设备MAC存在，则修改
            device.setDeviceId(old.getDeviceId());
            return this.updateSmDevice(device);
        }
    }

    @Override
    public List<DeviceMacSnVO> selectMacSnList(DeviceQuery query) {
        return deviceMapper.selectMacSnList(query);
    }

    @Override
    public DeviceVO selectOne(DeviceQuery query) {
        return deviceMapper.selectOne(query);
    }

    @Override
    public List<DeviceVO> selectUsingDevice(Long userId) {
        // 查询正在使用中的订单
//        List<TransactionBillVO> usingBills = transactionBillService.selectUsingBill(userId);
//        List<Long> deviceIds = usingBills.stream().map(TransactionBillVO::getDeviceId).distinct().collect(Collectors.toList());
        // 查询设备列表
        return this.selectByIds(null);
    }

    @Override
    public List<Long> selectDeviceUsingUser(Long deviceId) {
        // 正在使用的用户ID列表
        Set<Long> userIds = new HashSet<>();

//        // 充值订单
//        TransactionBillQuery query = new TransactionBillQuery();
//        query.setDeviceId(deviceId);
//        List<TransactionBillVO> transactionList = transactionBillService.selectUsingBill(query);
//        if (CollectionUtils.isNotEmptyElement(transactionList)) {
//            userIds.addAll(transactionList.stream().map(TransactionBillVO::getUserId).collect(Collectors.toList()));
//        }
//
//        // 时长订单
//        TimeBillQuery timeQuery = new TimeBillQuery();
//        timeQuery.setDeviceId(deviceId);
//        List<TimeBillVO> timeBillList = timeBillService.selectUsingList(timeQuery);
//        if (CollectionUtils.isNotEmptyElement(timeBillList)) {
//            userIds.addAll(timeBillList.stream().map(TimeBillVO::getUserId).collect(Collectors.toList()));
//        }

        return new ArrayList<>(userIds);
    }

    /**
     * 开关设备
     */
    @Override
    public boolean switchDevice(Long deviceId, boolean open,boolean isIgnore, String reason,boolean returnMac) {
        DeviceVO device = selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");
        if (open) {
            return iotService.open(device,isIgnore,reason,returnMac);
        } else {
            return iotService.close(device,isIgnore,reason,returnMac);
        }
    }

    @Override
    public int reboot(DeviceVO device, String reason) {
        ServiceUtil.assertion(device == null, "设备不存在");
        CommandResponse res = iotService.reboot(device, reason);
        return res != null && res.isSuccess() ? 1 : 0;
    }

    /**
     * 开门
     */
    @Override
    public int openDoor(DeviceVO device, String reason) {
        ServiceUtil.assertion(device == null, "设备不存在");
        CommandResponse res = iotService.openDoor(device, reason, true);
        return res != null && res.isSuccess() ? 1 : 0;
    }

    /**
     * 开关设施
     */
    @Override
    public boolean switchEquipment(Long equipmentId, boolean open,boolean isIgnore,String reason) {
        EquipmentVO vo = equipmentService.selectEquipmentByEquipmentId(equipmentId);
        ServiceUtil.assertion(vo == null, "设施不存在");
        ServiceUtil.assertion(!isIgnore && vo.getDeviceId()  == null, "未绑定设备");
        if(vo.getType().equals(FACILITY_TYPE_ROOM_DOOR) && !open){
            return true;
        }
        return controlDevice(vo.getDeviceId(),vo.getUnlockTime(),vo.getUnlockMode(),open, isIgnore, reason,false);
    }

    /**
     * 开关设施
     */
    @Override
    public int setTimeDevice(Long deviceId, long seconds,boolean isIgnore,String reason) {
        DeviceVO deviceVO = selectById(deviceId);
        ServiceUtil.assertion(!isIgnore && deviceVO  == null, "设备不存在");
        return setTime(deviceVO,seconds, isIgnore, reason);
    }

    @Override
    public int batchUpdateModel(DeviceBatchUpdateModelDTO dto) {
        return deviceMapper.batchUpdateModel(dto);
    }

    @Override
    public int updateServiceRate(DeviceVO data) {
        return deviceMapper.updateServiceRate(data);
    }

    @Override
    public int renewalRentTime(Long deviceId, int time, TimeUnit timeUnit) {
        long seconds = timeUnit.toSeconds(time);
        return deviceMapper.renewalRentTime(deviceId, seconds);
    }

    @Override
    public void pullDeviceInfo(Long deviceId) {
        pullDeviceInfo(Collections.singletonList(deviceId));
    }

    @Override
    public int resetTime(DeviceVO device, boolean required) {
        Integer result = transactionTemplate.execute(status -> {
            // 归零时间，将过期时间设置为现在
            int update = this.resetUpdateDbTime(device.getDeviceId());
            ServiceUtil.assertion(update != 1, "修改设备时间失败");

            // 物联网设备归零
            try {
                CommandResponse res = iotService.setTime(device, 0L,"");
                ServiceUtil.assertion(res == null, "设备归零失败，返回值为空");
                ServiceUtil.assertion(!res.isSuccess(), "设备归零失败，请检查设备是否在线或联系管理员");

//                boolean close = iotService.close(device);
//                ServiceUtil.assertion( !close, "设备关闭失败，请检查设备是否在线或联系管理员");
            } catch (Exception e) {
                log.warn("设备归零失败:{}, {}, {}", device.getDeviceId(), device.getSn(), e.getMessage());
                if (required) {
                    throw e;
                }
            }

            return update;
        });

        // 归零记录
//        if (result != null && result == 1 ) {
//            this.resetRecord(device, RecordTimeType.TIME, "设备归零");
//        }

        return result == null ? 0 : result;
    }

    private int resetUpdateDbTime(Long deviceId) {
        Device form = new Device();
        form.setDeviceId(deviceId);
        form.setExpireTime(LocalDateTime.now());
        form.setStatus(DeviceStatus.NORMAL.getStatus());
        return deviceMapper.updateSmDevice(form);
    }

    @Override
    public int resetEle(DeviceVO device, boolean required) {
        if (device == null) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            // 更新剩余电量
            int update = this.resetUpdateDbEle(device.getDeviceId());
            ServiceUtil.assertion(update != 1, "更新剩余电量失败");

            // 直接设置电量为0
            try {
                CommandResponse res = iotService.setEle(device, BigDecimal.ZERO);
                ServiceUtil.assertion(res == null, "归零电量失败，返回值为空");
                ServiceUtil.assertion(!res.isSuccess(), "归零电量失败，请检查设备是否在线或联系管理员");
            } catch (Exception e) {
                if (required) {
                    throw e;
                }
            }

            return update;
        });

//        // 归零记录
//        if (result != null && result == 1 ) {
//            this.resetRecord(device, RecordTimeType.ELE, "设备归零");
//        }

        return result == null ? 0 : result;
    }

//    private void resetRecord(DeviceVO device, RecordTimeType type, String reason) {
//        LoginUser loginUser;
//        try {
//            loginUser = SecurityUtils.getLoginUser();
//        } catch (Exception e) {
//            loginUser = null;
//        }
//
//        LoginUser finalLoginUser = loginUser;
//        scheduledExecutorService.schedule(() -> {
//            RecordTime record = null;
//            // 电量
//            if (RecordTimeType.ELE.equals(type)) {
//                int amount = device.getSurplusEle().negate().intValue();
//                record = recordTimeConverter.toRecordTime(
//                        device,
//                        amount,
//                        reason,
//                        finalLoginUser,
//                        RecordTimeType.ELE.getType()
//                );
//            }
//            // 时长
//            else if (RecordTimeType.TIME.equals(type)) {
//                Duration duration = Duration.between(LocalDateTime.now(), device.getExpireTime());
//                long seconds = duration.getSeconds() > 0 ? duration.getSeconds() : 0;
//                record = recordTimeConverter.toRecordTime(device, -seconds, "设备归零", finalLoginUser, RecordTimeType.TIME.getType());
//            }
//            if (record != null) {
//                recordTimeService.insertRecordTime(record);
//            }
//        }, 0, TimeUnit.SECONDS);
//    }

    private int resetUpdateDbEle(Long deviceId) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setSurplusEle(BigDecimal.ZERO);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int resetEle(Long deviceId, boolean required) {
        // 获取设备信息
        DeviceVO device = selectById(deviceId);
        return resetEle(device, required);
    }

    @Override
    public int resetTimeWithBill(Long deviceId, boolean requiredIot) {
        if (deviceId == null) {
            return 0;
        }
        // 拉取设备信息
        pullDeviceInfo(deviceId);

        DeviceVO device = selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");

        // 尝试关闭该设备未结束的所有订单
//        TransactionBillQuery query = new TransactionBillQuery();
//        query.setStatusList(TransactionBillStatus.canClose());
//        query.setDeviceId(device.getDeviceId());
//        query.setIsFinished(false);   // 未结束的订单都会被关闭
//        query.setSuitFeeTypes(SuitFeeType.rechargeTimeList());
//        List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
//        try {
//            int closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device);
//            ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
//        } catch (Exception e) {
//            log.error("关闭订单失败:{}" , e.getMessage());
//        }

        // 发送命令
        int reset = this.resetTime(device, requiredIot);
        ServiceUtil.assertion(reset != 1, "设备时长归零失败");

        return 1;
    }

    @Override
    public int resetEleWithBill(Long deviceId, boolean requiredIot) {
        if (deviceId == null) {
            return 0;
        }
        // 拉取设备信息
        pullDeviceInfo(deviceId);

        DeviceVO device = selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");

        // 尝试关闭该设备未结束的所有订单
//        TransactionBillQuery query = new TransactionBillQuery();
//        query.setStatusList(TransactionBillStatus.canClose());
//        query.setDeviceId(device.getDeviceId());
//        query.setIsFinished(false);   // 未结束的订单都会被关闭
//        query.setSuitFeeTypes(SuitFeeType.rechargeCountList());
//        List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
//        try {
//            int closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device);
//            ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
//        } catch (Exception e) {
//            log.error("关闭订单失败:{}" , e.getMessage());
//        }

        // 发送命令
        int reset = this.resetEle(device, requiredIot);
        ServiceUtil.assertion(reset != 1, "设备电量归零失败");

        return 1;
    }

    @Override
    public boolean addEle(Long deviceId, BigDecimal ele, boolean withIot, String reason) {
        DeviceVO device = selectById(deviceId);
        if (device == null) {
            return false;
        }

        boolean result = false;
        if (withIot) {
            CommandResponse res = iotService.addEle(device, ele);
            result = res.isSuccess();
        }

        // 记录下操作日志
//        if (result) {
//            LoginUser loginUser = SecurityUtils.getLoginUser();
//            scheduledExecutorService.schedule(() -> {
//                recordTimeService.insertRecordTime(recordTimeConverter.toRecordTime(device, ele.longValue(), reason, loginUser, RecordTimeType.ELE.getType()));
//            }, 0, TimeUnit.SECONDS);
//        }

        return true;
    }

    @Override
    public int updateLastRecoverTime(Long deviceId, Long lastRecoverTime) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setLastRecoverTime(lastRecoverTime);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int setTime(DeviceVO device, LocalDateTime expireTime, boolean withIot, int tryCount) {
        if (device == null || StringUtils.isBlank(device.getMac()) || expireTime == null) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            int update = this.updateExpireTime(device.getDeviceId(), expireTime);
            ServiceUtil.assertion(update != 1, "更新数据失败");

            if (withIot) {
                Duration between = Duration.between(LocalDateTime.now(), expireTime);
                long seconds = between.getSeconds();
                if (seconds > 0) {
                    CommandResponse res = iotService.trySetTime(device, seconds, tryCount, "设置时长");
                    if (!res.isSuccess()) {
                        log.error("操作设备设置时长{}失败：{}", device.getDeviceId(), res.getMsg());
                        throw new ServiceException(res.getMsg());
                    }
                }
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    private int updateExpireTime(Long deviceId, LocalDateTime expireTime) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setExpireTime(expireTime);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int setWifi(DeviceWifiDTO dto) {
        DeviceVO device = selectById(dto.getDeviceId());
        if (device == null) {
            return 0;
        }
        CommandResponse res = iotService.setWifi(device, dto.getWifiName(), dto.getWifiPwd());
        ServiceUtil.assertion(!res.isSuccess(), "设置WIFI失败：" + res.getMsg());

        return 1;
    }

    @Override
    public DeviceVO selectByAnyMac(String mac) {
        return deviceMapper.selectByAnyMac(mac);
    }

    @Override
    public void pullDeviceInfo(DeviceVO device) {
        this.pullDeviceInfoList(Collections.singletonList(device));
    }

    @Override
    public int unbindAgent(Long deviceId) {
        DeviceVO device = selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");

        Integer result = transactionTemplate.execute(status -> {
            // 解绑
            int unbind = deviceMapper.unbindAgent(deviceId);
            ServiceUtil.assertion(unbind != 1, "解绑失败，请刷新后重试");

            // 新增解绑记录
            deviceBindRecordService.record(device.getUserId(), deviceId, BindRecordType.UNBIND, BindRecordUserType.MCH);

            return unbind;
        });

        return result == null ? 0 : result;
    }

    @Override
    public Boolean bindMch(Long deviceId, Long mchId) {
        DeviceVO device = this.selectById(deviceId);
        Long storeId = storeService.selectStoreIdByMerchantId(mchId);
        return this.bind(storeId, mchId, device);
    }

    @Override
    public BigDecimal selectMaxAgentServiceRate(DeviceQuery query) {
//        query.setServiceMode(DeviceServiceMode.AGENT.getMode());
        return deviceMapper.selectMaxAgentServiceRate(query);
    }

    @Override
    public int bindAgent(Long deviceId, Long agentId, BigDecimal agentServiceRate) {
        if (deviceId == null || agentId == null || agentServiceRate == null) {
            return 0;
        }
//        ServiceUtil.assertion(!userValidator.isAgent(agentId), "该用户不是代理商，无法绑定为设备代理商");

        Integer result = transactionTemplate.execute(status -> {
            // 绑定
            int bind = deviceMapper.bindAgent(deviceId, agentId, agentServiceRate);
            ServiceUtil.assertion(bind != 1, "绑定代理商失败，请刷新后重试");

            // 后校验
            DeviceVO vo = this.selectById(deviceId);
//            deviceValidator.checkAgent(vo);

            // 添加绑定记录
//            int record = smDeviceBindRecordService.record(agentId, deviceId, BindRecordType.BIND, BindRecordUserType.AGENT);
//            ServiceUtil.assertion(record != 1, "添加绑定记录失败");

            return bind;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int setEle(DeviceVO device, BigDecimal ele, int tryCount) {
        if (device == null || ele == null || tryCount <= 0) {
            log.error("设置电量失败：参数为空");
            return 0;
        }
        Integer result = transactionTemplate.execute(status -> {
            int update = this.updateSurplusEle(device.getDeviceId(), ele);
            ServiceUtil.assertion(update != 1, "更新设备数据失败");

            CommandResponse res = iotService.trySetEle(device, ele, tryCount);
            if (res == null || !res.isSuccess()) {
                log.error("操作设备设置电量{}失败：{}", device.getDeviceId(), res);
                ServiceUtil.assertion(res == null, "充值电量失败");
                IotHttpStatus statusResult = IotHttpStatus.convertByCode(res.getCode());
                ServiceUtil.assertion(!res.isSuccess(), "充值电量失败：" + statusResult.getMsg());
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int setTime(DeviceVO device, long seconds, int tryCount) {
        return this.setTime(device, LocalDateTime.now().plusSeconds(seconds), true, tryCount);
    }

    @Override
    public int setTime(DeviceVO device, long seconds, boolean isIgnore,String reason) {
        CommandResponse res1 = iotService.setTime(device, seconds,reason);
        ServiceUtil.assertion(!res1.isSuccess(), "设备设置时长失败:" + res1.getMsg());
        return  1;
    }

    @Override
    public int km(DeviceVO device, long seconds,String reason, boolean returnMac) {
        CommandResponse res1 = iotService.km(device, seconds,reason,returnMac);
        ServiceUtil.assertion(!res1.isSuccess(), "开门失败:" + res1.getMsg());
        return  1;
    }

    @Override
    public int gm(DeviceVO device, long seconds,String reason, boolean returnMac) {
        CommandResponse res1 = iotService.gm(device, seconds,reason,returnMac);
        ServiceUtil.assertion(!res1.isSuccess(), "关门失败:" + res1.getMsg());
        return  1;
    }

    @Override
    public int setTtme(DeviceVO device, long seconds, String reason) {
        CommandResponse res1 = iotService.setTtme(device, seconds,reason);
        ServiceUtil.assertion(!res1.isSuccess(), "设备设置时长失败:" + res1.getMsg());
        return  1;
    }

    /**
     * 投放
     */
    @Override
    public int placementDevice(PlacementDTO dto) {
        ServiceUtil.assertion(StringUtils.isBlank(dto.getSn()) && dto.getDeviceId() == null, "设备sn或设备id不能同时为空");
        DeviceVO deviceVO = null;
        if (dto.getDeviceId() == null) {
            deviceVO = selectBySn(dto.getSn());
        }else{
            deviceVO = selectById(dto.getDeviceId());
        }
        ServiceUtil.assertion(deviceVO == null,"设备不存在");
        ServiceUtil.assertion(DEVICE_PLACEMENT_STATUS_PLACED.equals(deviceVO.getPlacementStatus()),"设备已投放，请先解绑！");

        String type = dto.getType();
        DeviceVO finalDeviceVO = deviceVO;
        Boolean execute = transactionTemplate.execute(e -> {
            Long storeId;
            // 根据type判断更新哪个表
            if (FACILITY_TYPE_TOILET_DOOR.equals(type)) {// 卫生间--查卫生间表
                ToiletVO toiletVO = toiletService.selectToiletByToiletId(dto.getObjId());
                ServiceUtil.assertion(toiletVO == null,"卫生间不存在");
                ServiceUtil.assertion(toiletVO.getDeviceId() != null,"该卫生间已绑定过设备");
                storeId = toiletVO.getStoreId();

                Toilet toilet = new Toilet();
                toilet.setDeviceId(finalDeviceVO.getDeviceId());
                toilet.setToiletId(dto.getObjId());
                int i = toiletService.updateToilet(toilet);
                ServiceUtil.assertion(i == 0,"投放卫生间失败");
            }else if(FACILITY_TYPE_GATE.equals(type)){// 大门--查店铺
                StoreVO storeVO = storeService.selectEStoreByStoreId(dto.getObjId());
                ServiceUtil.assertion(storeVO == null,"店铺不存在");
                ServiceUtil.assertion(storeVO.getGateId() != null,"该店铺已绑定过大门");
                storeId = storeVO.getStoreId();

                Store updateStore = new Store();
                updateStore.setGateId(finalDeviceVO.getDeviceId());
                updateStore.setGateSn(finalDeviceVO.getSn());
                updateStore.setStoreId(dto.getObjId());
                int i = storeService.updateEStore(updateStore);
                ServiceUtil.assertion(i == 0,"投放大门失败");
            }else{// 其他的都是设施表
                EquipmentVO equipmentVO = equipmentService.selectEquipmentByEquipmentId(dto.getObjId());
                ServiceUtil.assertion(equipmentVO == null,"设施不存在");
                ServiceUtil.assertion(equipmentVO.getDeviceId() != null,"该设施已绑定设备");
                RoomVO room = roomService.selectERoomByRoomId(equipmentVO.getRoomId());
                ServiceUtil.assertion(room == null,"房间不存在");
                storeId = room.getStoreId();

                // 更新设施
                int i = updateEquipment(dto.getObjId(), finalDeviceVO.getDeviceId(), room);
                ServiceUtil.assertion(i == 0, "投放设施失败");
            }

            // todo  更新设备信息  投放状态
            Device device = new Device();
            device.setPlacementStatus(DEVICE_PLACEMENT_STATUS_PLACED);
            device.setPlacementType(dto.getType());
            device.setDeviceId(finalDeviceVO.getDeviceId());
            device.setStoreId(storeId);

            // 更新设备信息
            int i = deviceMapper.updateSmDevice(device);
            ServiceUtil.assertion(i == 0, "更新设备信息失败");

            //新增解绑记录
            int record = deviceBindRecordService.record(getUserId(), device.getDeviceId(), BindRecordType.PLACEMENT, BindRecordUserType.MCH);
            ServiceUtil.assertion(record != 1, "添加绑定记录失败");
            return true;
        });
        if(Boolean.FALSE.equals(execute))throw new ServiceException("【完成保洁】失败");
        return 1;
    }

    private int updateEquipment(Long equipmentId, Long deviceId, RoomVO room) {
        Equipment equipment = new Equipment();
        equipment.setEquipmentId(equipmentId);
        equipment.setDeviceId(deviceId);
        equipment.setStoreId(room.getStoreId());
        equipment.setStoreName(room.getStoreName());
        return equipmentService.updateEquipment(equipment);
    }

    /**
     * 下架
     */
    @Override
    public int offline(Long deviceId) {
        DeviceVO deviceVO = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(DEVICE_STATUS_PLACEMENT_NOT_PLACED.equals(deviceVO.getPlacementStatus()), "设备未投放，无需下架！");

        // 根据设备id查询设备所关联的设施
        List<Long> roomIds = equipmentService.selectRoomIdByDeviceId(deviceId);
        boolean inProgressOrderByIds = orderValidator.getInProgressOrderByIds(roomIds);
        ServiceUtil.assertion(inProgressOrderByIds, "操作失败,该设备还有正在进行中订单");

        Boolean execute = transactionTemplate.execute(e -> {
            int i = offlineDevice(deviceId, deviceVO.getPlacementType());
            ServiceUtil.assertion(i == 0, "设备下架失败，请刷新后重试");
            return true;
        });
        if(Boolean.FALSE.equals(execute))throw new ServiceException("【设备下架】失败");
        return 1;
    }

    private int offlineDevice(Long deviceId, String placementType) {
        int i;
        if (FACILITY_TYPE_TOILET_DOOR.equals(placementType)) {// 卫生间--查卫生间表
            i = toiletService.offlineDeviceByDeviceId(deviceId);
        }else if(FACILITY_TYPE_GATE.equals(placementType)){// 大门--查店铺
            i = storeService.offlineDeviceByDeviceId(deviceId);
        }else {// 其他的都是设施表
            i = equipmentService.offlineDeviceByDeviceId(deviceId);
        }
//        ServiceUtil.assertion(i == 0, "设备解绑失败，请刷新后重试");

        // 清除投放状态
        int i1 = deviceMapper.cleanPlacementStatus(deviceId);
        ServiceUtil.assertion(i1 != 1, "【下架】清除投放状态失败");

        //新增解绑记录
        int record = deviceBindRecordService.record(getUserId(), deviceId, BindRecordType.OFFLINE, BindRecordUserType.MCH);
        ServiceUtil.assertion(record != 1, "添加绑定记录失败");

        return i;
    }

    /**
     * 房间开关
     */
    @Override
    public int roomAllSwitchDevice(Long roomId, Boolean open, String reason) {
        try {
            // 通过房间id查询所有设备id
            List<EquipmentVO> equipmentVOS = equipmentService.selectEquipmentListByRoomId(roomId);
            for (EquipmentVO equ : equipmentVOS) {
                if(FACILITY_TYPE_ROOM_DOOR.equals(equ.getType())){
                    if(equ.getDeviceId() != null && open){
                        controlDoor(equ.getDeviceId(), equ.getUnlockTime(), equ.getUnlockMode(),true, reason,false);
                    }
                }else{
                    switchEquipment(equ.getEquipmentId(),open,true,reason);
                }
            }
        } catch (Exception e) {
            log.error("【房间开关】失败", e);
            return 1;
        }
        return 1;
    }

    /**
     * 下单成功后房间全开
     */
    @Override
    public int roomAllDeviceSetTime(OrderVO orderVO, String reason) {
        Long roomId = orderVO.getRoomId();
        Date reserveStartTime = orderVO.getReserveStartTime();
        Date reserveEndTime = orderVO.getReserveEndTime();
        long seconds = DateUtils.timeDifferenceInSeconds(reserveEndTime, reserveStartTime);
        // 通过房间id查询所有设备id
        List<Long> deviceIds =  roomService.selectAllDeviceIdsByRoomId(roomId);
        for (Long deviceId : deviceIds) {
            setTimeDevice(deviceId,seconds,true,reason);
        }

        // 房间门单独打开
        EquipmentVO roomDoor = equipmentService.selectDoorByRoomId(roomId, FACILITY_TYPE_ROOM_DOOR);
        boolean b = controlDoor(roomDoor.getDeviceId(), roomDoor.getUnlockTime(), roomDoor.getUnlockMode(),true, reason,false);
        log.info("【下单成功后房间全开】打开房间门{}", b ? "成功" : "失败");
        return 1;
    }

    /**
     * 大厅开关
     */
    @Override
    public int hallEquAllSwitchDevice(Long storeId, Boolean open, String reason) {
        // 通过房间id查询所有设备id
        List<Long>  equipmentIds = roomService.selectAllHallEquIdsByStoreId(storeId);
        for (Long equipmentId : equipmentIds) {
            switchEquipment(equipmentId,open,true,reason);
        }
        return 1;
    }

    @Override
    public String selectSnByMac(String mac) {
        return deviceMapper.selectSnByMac(mac);
    }

    private int updateSurplusEle(Long deviceId, BigDecimal ele) {
        if (deviceId == null || ele == null) {
            return 0;
        }
        Device data = new Device();
        data.setSurplusEle(ele);
        data.setDeviceId(deviceId);
        return deviceMapper.updateSmDevice(data);
    }


    @Override
    public boolean addTime(Long deviceId, long seconds, boolean withIot) {
        ServiceUtil.assertion( seconds < 0, "增加的时长不允许小于0");

        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(!StringUtils.hasText(device.getMac()), "设备MAC号为空");
        ServiceUtil.assertion(DeviceStatus.FIXING.getStatus().equals(device.getStatus()), "设备正在维修中，无法使用");

        Boolean result = transactionTemplate.execute(status -> {
            // 更新数据库时长
            int updateCount = deviceMapper.addTime(deviceId, seconds);
            ServiceUtil.assertion(updateCount != 1, "增加时长失败，请刷新后重试");

            // 修改状态为使用中
            changeStatus(deviceId, DeviceStatus.USING);

            // 物联网设备增加时长
            if (withIot) {
                DeviceVO newDevice = selectById(deviceId);
                long betweenSeconds = Duration.between(LocalDateTime.now(), newDevice.getExpireTime()).getSeconds();
                if (betweenSeconds > 0) {
                    CommandResponse rechargeResult = iotService.setTime(device, betweenSeconds,"");
                    ServiceUtil.assertion(!rechargeResult.isSuccess(), "设备充值失败，请检查设备是否在线");
                }
            }
            return Boolean.TRUE;
        });

        // 是否成功
        boolean success = result != null && result;
        if (success) {
            // 拉取设备信息
            this.pullDeviceInfoAsync(Collections.singletonList(deviceId), 3, TimeUnit.SECONDS);

            // 时长结束后修改设备状态
//            scheduledExecutorService.schedule(()-> {
//                freshStatus(deviceId);
//            }, seconds, TimeUnit.SECONDS);
        }

        return success;
    }


//    private void freshStatus(Long deviceId) {
//        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
//        freshStatus(device);
//    }

    @Override
    public void freshStatus(DeviceVO device) {
        if (device == null || device.getExpireTime() == null) {
            return;
        }
        if (!DeviceStatus.USING.getStatus().equals(device.getStatus())) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        Duration between = Duration.between(now, device.getExpireTime());

        // 如果已过期，则调整设备为正常状态
        int i = deviceMapper.changeTimeoutStatus(device.getDeviceId());
        if (i == 1) {
            return;
        }

//        // 如果没有过期，则延迟至过期时间继续查询
//        if (between.getSeconds() > 0) {
//            scheduledExecutorService.schedule(() -> {
//                freshStatus(device.getDeviceId());
//            }, between.getSeconds() , TimeUnit.SECONDS);
//        }
    }

    /**
     * 设备心跳，用于检测是否在线
     */
    @Override
    public void deviceHeartBeat() {
        log.info("device heart beat running...");
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(new DeviceQuery());
        if (CollectionUtils.isEmpty(list)) {
            log.info("device list is empty");
            return;
        }
        for (DeviceVO device : list) {
            this.setOnlineStatus(device);
        }
        int i = deviceMapper.batchUpdateOnlineStatus(list);
        log.info("update count: {}", i);
    }


    private int changeStatus(Long deviceId, DeviceStatus status) {
        Device device = new Device();
        device.setDeviceId(deviceId);
        device.setStatus(status.getStatus());
        return deviceMapper.updateSmDevice(device);
    }

    @Override
    public int logicDel(List<Long> deviceIds) {
        // 校验
        this.validatePreLogicDelete(deviceIds);

        // 拉取最新数据
        this.pullDeviceInfo(deviceIds);
        // 查询设备列表
        List<DeviceVO> deviceList = this.selectByIds(deviceIds);

        Integer result = transactionTemplate.execute(status -> {
            // 关闭设备订单
            for (Long deviceId : deviceIds) {
                if (deviceId == null) {
                    continue;
                }

                DeviceVO device = deviceList.stream().filter(item -> Objects.equals(item.getDeviceId(), deviceId)).findFirst().orElse(null);
                if (device == null) {
                    continue;
                }

                // 关闭设备未结束的订单
//                TransactionBillQuery query = new TransactionBillQuery();
//                query.setType(TransactionBillType.RECHARGE.getType());
//                query.setStatusList(TransactionBillStatus.canClose());
//                query.setDeviceId(deviceId);
//                query.setIsFinished(false);
//                List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
//                int closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device);
//                ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
            }

            int i = deviceMapper.logicDel(deviceIds);
            ServiceUtil.assertion(i != deviceIds.size(), "删除设备失败,请刷新后重试");

            return i;
        });

        return result == null ? 0 : result;
    }

    @Override
    public Map<Long, List<DeviceVO>> selectGroupByModelIds(List<Long> modelIds) {
        if (CollectionUtils.isEmpty(modelIds)) {
            return Collections.emptyMap();
        }
        DeviceQuery dto = new DeviceQuery();
        dto.setModelIds(modelIds);
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.groupingBy(DeviceVO::getModelId));
    }

    /**
     * 切换设备通电状态（通/断电）
     *
     * @param device 设备id
     * @param status 通电状态
     */
    @Override
    public boolean changePowerStatus(DeviceVO device, DevicePowerStatus status,String reason) {
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");

        if (DevicePowerStatus.ON.equals(status)) {
            iotService.open(device,false,reason,false);
        } else if (DevicePowerStatus.OFF.equals(status)) {
            iotService.close(device,false,reason,false);
        } else {
            throw new ServiceException("不支持的操作");
        }

        // 修改数据库通断电状态
        Device changePowerForm = new Device();
        changePowerForm.setDeviceId(device.getDeviceId());
        changePowerForm.setPowerStatus(status.getStatus());
        deviceMapper.updateSmDevice(changePowerForm);

        return true;
    }

    /**
     * 拉取设备信息，并保存到数据库
     * @param deviceIds 设备id列表（为空则查询所有）
     */
    @Override
    @SneakyThrows
    public void pullDeviceInfo(List<Long> deviceIds) {
        // 查询设备列表
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        this.pullDeviceInfoList(list);
    }

    @Override
    public void pullDeviceInfoList(List<DeviceVO> list) {
        if (CollectionUtils.isEmptyElement(list)) {
            return;
        }

        // 拼接正在使用中的订单数量
        deviceAssembler.assembleUsingBillCount(list);

        // 列表根据产品ID分组
        Map<String, List<DeviceVO>> group = list.stream().collect(Collectors.groupingBy(DeviceVO::getModelProductId));

        for (Map.Entry<String, List<DeviceVO>> entry : group.entrySet()) {

            // 批量获取设备信息
            List<DeviceVO> groupDeviceList = entry.getValue();
            String productId = entry.getKey();
            List<IotDeviceInfo> iotList = iotService.getDeviceInfo(groupDeviceList, productId);

            // 构造更新设备信息
            for (DeviceVO device : groupDeviceList) {
                // 获取数据点信息
                IotDeviceInfo deviceInfo = iotList.stream()
                        .filter(item -> Objects.equals(device.getMac(), item.getMac()) || Objects.equals(device.getMac2(), item.getMac()))
                        .findFirst().orElse(null);

                log.info("拉取的deviceInfo: {}", JSON.toJSON(deviceInfo));
                // 更新设备信息
                device.setDeviceId(device.getDeviceId());
                if (deviceInfo != null) {
                    device.setLastPullTime(deviceInfo.getAt());
                    device.setPowerStatus(deviceInfo.getS());
                    device.setRemainTime(deviceInfo.getTime());
                    device.setRealTimePower(deviceInfo.getP());
                    device.setVoltage(deviceInfo.getV());
                    device.setElectricity(deviceInfo.getA());
                    device.setVersion(deviceInfo.getVersion());
                    device.setVxs(deviceInfo.getVxs());

                    // 判断是否支持WIFI，支持则更新
                    if (ModelTag.hasTag(device.getModelTags(), ModelTag.WIFI)) {
                        device.setWifi(deviceInfo.getWifi());
                    }

                    // 判断是否支持电量，支持则更新
                    if (ModelTag.hasTag(device.getModelTags(), ModelTag.ELE)) {
                        device.setSurplusEle(deviceInfo.getM());
                    } else {
                        device.setSurplusEle(BigDecimal.ZERO);
                    }

                    // 总用电量
//                    if (deviceInfo.getW() != null) {
//                        // 若有数据点中的值大于数据库中的值，则更新
//                        if (deviceInfo.getW().compareTo(device.getTotalElectriQuantity()) >= 0) {
//                            device.setTotalElectriQuantity(deviceInfo.getW());
//                        }
//                        // 若数据点小于数据库中的值，则发命令恢复物联网设备的总用电量
//                        else {
//                            scheduledExecutorService.schedule(() -> {
//                                String reason = "拉取设备信息时，恢复总用电量,数据库值:" + device.getTotalElectriQuantity() + "，OneNet值：" + deviceInfo.getW();
//                                iotService.setTotalEle(device, device.getTotalElectriQuantity().multiply(new BigDecimal(1000)), reason);
//                            }, 0, TimeUnit.SECONDS);
//                        }
//                    }
                }

                // 判断设备是否正在使用
                // 若开关开启，则正在使用
                boolean hasOpen = DevicePowerStatus.ON.getStatus().equals(device.getPowerStatus());
                // 若设备有正在使用中的订单，则正在使用
                boolean hasUsingBill = device.getUsingBillCount() != null && device.getUsingBillCount() > 0;
                if (hasOpen || hasUsingBill) {
                    device.setStatus(DeviceStatus.USING.getStatus());
                } else {
                    device.setStatus(DeviceStatus.NORMAL.getStatus());
                }

            }
        }

        // 异步更新设备信息
        for (DeviceVO device : list) {
            scheduledExecutorService.schedule(() -> {
                log.info("异步更新数据库的device: {}", device);
                // 获取并设置在线状态
                this.setOnlineStatus(device);
                // 更新数据库
                this.updateIotInfo(device);
            }, 0, TimeUnit.SECONDS);
        }
    }

    private void setOnlineStatus(DeviceVO device) {
        // 是否在线
        String onlineStatus1 = iotService.getOnlineStatus(device.getMac(), device.getProductId());
        String onlineStatus2 = iotService.getOnlineStatus(device.getMac2(), device.getProductId());

        // 若有一个没获取到锁，则不更新状态
        boolean unlockMac1 = StringUtils.isNotBlank(device.getMac()) && onlineStatus1 == null;
        boolean unlockMac2 = StringUtils.isNotBlank(device.getMac2()) && onlineStatus2 == null;
        if (unlockMac1 || unlockMac2){
            device.setOnlineStatus(null);
            device.setOnlineStatus1(null);
            device.setOnlineStatus2(null);
            device.setLastOnlineTime(null);
        } else {
            device.setOnlineStatus1(onlineStatus1);
            device.setOnlineStatus2(onlineStatus2);
            if (DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus1) || DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus2)) {
                device.setOnlineStatus(DeviceOnlineStatus.ONLINE.getStatus());
                device.setLastOnlineTime(LocalDateTime.now());
            } else {
                device.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());
            }
        }
    }

    /**
     * 更新物联网信息
     */
    private int updateIotInfo(Device device) {
        Device data = new Device();
        data.setDeviceId(device.getDeviceId());
        data.setLastPullTime(device.getLastPullTime());
        data.setTotalElectriQuantity(device.getTotalElectriQuantity());
        data.setPowerStatus(device.getPowerStatus());
        data.setRemainTime(device.getRemainTime());
        data.setRealTimePower(device.getRealTimePower());
        data.setVoltage(device.getVoltage());
        data.setElectricity(device.getElectricity());
        data.setWifi(device.getWifi());
        data.setSurplusEle(device.getSurplusEle());
        data.setStatus(device.getStatus());
        data.setOnlineStatus(device.getOnlineStatus());
        data.setLastOnlineTime(device.getLastOnlineTime());
        return deviceMapper.updateSmDevice(data);
    }

    /**
     * 异步延迟执行拉取设备信息
     * @param deviceIds 设备id列表
     * @param delay 延迟时间
     * @param unit 时间单位
     */
    private void pullDeviceInfoAsync(List<Long> deviceIds, long delay, TimeUnit unit) {
        scheduledExecutorService.schedule(() -> {
            log.info("异步拉取设备信息 start");
            this.pullDeviceInfo(deviceIds);
            log.info("异步拉取设备信息 end");
        }, delay, unit);
    }



    @Override
    public Boolean bindBySn(Long storeId, Long userId, String deviceNo) {
        DeviceVO device = selectBySn(deviceNo);
        return this.bind(storeId, userId, device);
    }

    @Override
    public Boolean bindByMac(Long storeId, Long userId, String mac) {
        DeviceVO device = selectByMac(mac);
        return this.bind(storeId, userId, device);
    }

    private Boolean bind(Long storeId, Long userId, DeviceVO device) {
        ServiceUtil.assertion(device == null, "设备未录入");
        // 如果userId不为空，
        ServiceUtil.assertion(device.getUserId() != null, "该设备已被绑定");

        transactionTemplate.execute(status -> {
            // 绑定
            int updateCount = deviceMapper.bind(device.getDeviceId(), storeId, userId);
            ServiceUtil.assertion(updateCount != 1, "当前设备信息已变更，请刷新后重试");

            // 将用户更新变成商户，并将全局的配置加入用户提现渠道中
            UserVO userVO = userService.selectUserById(userId);
            if(!userVO.getUserType().equals(USER_TYPE_MERCHANT)){
                int i = userService.becomeMch(userId);
                ServiceUtil.assertion(i != 1, "更新商户失败");

                // 全局的配置加入用户提现渠道中
                userWithdrawalChannel(userVO.getUserId());
            }

            // 记录绑定记录
            int record = deviceBindRecordService.record(userId, device.getDeviceId(), BindRecordType.BIND, BindRecordUserType.MCH);
            ServiceUtil.assertion(record != 1, "添加绑定记录失败");

            return updateCount;
        });

        return Boolean.TRUE;
    }

    private void userWithdrawalChannel(Long userId) {
        ChannelWithdrawQuery channelWithdrawQuery = new ChannelWithdrawQuery();
        channelWithdrawQuery.setEnabled(true);
        List<ChannelWithdrawVO> channelWithdrawVOS = channelWithdrawService.selectChannelWithdrawList(channelWithdrawQuery);
        int i1 = userWithdrawService.deleteRlUserWithdrawByUserChannelId(userId);
        createUserWithdrawal(userId, channelWithdrawVOS);
    }

    private void createUserWithdrawal(Long userId, List<ChannelWithdrawVO> channelWithdrawVOS) {
        for (ChannelWithdrawVO channelWithdrawVO : channelWithdrawVOS) {
            UserWithdraw rlUserWithdraw = new UserWithdraw();
            rlUserWithdraw.setHandlingChargeType(channelWithdrawVO.getHandlingChargeType());
            rlUserWithdraw.setWithdrawHandlingCharge(channelWithdrawVO.getWithdrawHandlingCharge());
            rlUserWithdraw.setUserId(userId);
            rlUserWithdraw.setChannelId(channelWithdrawVO.getChannelId());
            rlUserWithdraw.setIsOpen(true);
            rlUserWithdraw.setMaxAmount(channelWithdrawVO.getMaxAmount());
            rlUserWithdraw.setMinAmount(channelWithdrawVO.getMinAmount());
            rlUserWithdraw.setName(channelWithdrawVO.getName());
            rlUserWithdraw.setPicture(channelWithdrawVO.getPicture());
            rlUserWithdraw.setIsNeedCode(channelWithdrawVO.getIsNeedCode());
            int i = userWithdrawService.insertRlUserWithdraw(rlUserWithdraw);
            ServiceUtil.assertion(i==0,"创建用户提现渠道失败：userId:"+ userId);
        }
    }

    /**
     * 根据mac号查询数据
     * @param mac
     * @return
     */
    @Override
    public DeviceVO selectByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        }
        return deviceMapper.selectSimpleSmDeviceByMac(mac);
    }

    @Override
    public int resetWithBill(Long deviceId) {
        ServiceUtil.assertion(deviceId == null, "设备id不能为空");

        // 拉取最新设备信息
        pullDeviceInfo(deviceId);

        // 获取设备信息
        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");
//        ServiceUtil.assertion(!UserUtil.hasFrontUser(device.getUserId()), "您不是该设备的商户，无法进行该操作");

        Integer result = transactionTemplate.execute(status -> {
            // 尝试关闭该设备未结束的所有订单
//            TransactionBillQuery query = new TransactionBillQuery();
//            query.setStatusList(TransactionBillStatus.canClose());
//            query.setDeviceId(device.getDeviceId());
//            query.setIsFinished(false);   // 未结束的订单都会被关闭
//            List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
//            try {
//                int closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device);
//                ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
//            } catch (Exception e) {
//                log.error("关闭订单失败: {}", e.getMessage());
//            }

            // 修改设备信息
            int timeUpdate = this.resetUpdateDbTime(deviceId);
            ServiceUtil.assertion(timeUpdate != 1, "修改设备时间失败");
            int eleUpdate = this.resetUpdateDbEle(deviceId);
            ServiceUtil.assertion(eleUpdate != 1, "修改设备电量失败");

            // 发送命令
//            int reset = iotService.setTimeAndEle(device, 0L, BigDecimal.ZERO);
//            ServiceUtil.assertion(reset != 1, "归零失败");
            CommandResponse res1 = iotService.setTime(device, 0L,"");
            ServiceUtil.assertion(!res1.isSuccess(), "设备时长归零失败:" + res1.getMsg());
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                log.error("设备归零等待时间异常:{}", e.getMessage());
                throw new ServiceException(e.getMessage());
            }
            CommandResponse res2 = iotService.setEle(device, BigDecimal.ZERO);
            ServiceUtil.assertion(!res2.isSuccess(), "设备电量归零失败:" + res2.getMsg());

            return 1;
        });

//        if (result != null && result == 1) {
//            this.resetRecord(device, RecordTimeType.TIME, "设备归零");
//            this.resetRecord(device, RecordTimeType.ELE, "设备归零");
//        }

        return result == null ? 0 : result;
    }

    /**
     * 设备是否已经被绑定
     *
     * @param mac
     * @return
     */
    @Override
    public int bindCodeByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return Constants.DEVICE_NOT_FOUND;
        }
        DeviceVO device = deviceMapper.selectSimpleSmDeviceByMac(mac);
        return isBindCode(device);
    }

    private int isBindCode(DeviceVO device) {
        if (device == null) {
            return Constants.DEVICE_NOT_FOUND;
        }
        if (device.getUserId() == null) {
            return Constants.DEVICE_NOT_BIND;
        } else {
            return Constants.DEVICE_BINDED;
        }
    }

    /**
     * 根据SN码查询设备是否已经绑定
     *
     * @param deviceNo
     * @return
     */
    @Override
    public int bindCodeBySn(String deviceNo) {
        if (StringUtils.isBlank(deviceNo)) {
            return Constants.DEVICE_NOT_FOUND;
        }
        DeviceVO device = deviceMapper.selectSimpleSmDeviceBySn(deviceNo);
        return isBindCode(device);
    }

//    /**
//     * 更新设备服务费
//     *
//     * @param userId
//     * @param serviceRate
//     */
//    @Override
//    public int updateServiceRateByUserId(Long userId, BigDecimal serviceRate) {
//        if (userId == null) {
//            return 0;
//        }
////        List<StoreVo> list = storeService.selectByUserId(userId);
////        if (CollectionUtils.isEmpty(list)) {
////            return 0;
////        }
//        return deviceMapper.updateServiceRateByStores(list.stream().map(StoreVo::getStoreId).collect(Collectors.toList()), serviceRate);
//    }

    @Override
    public int updateByIot(Device device) {
        device.setLastPullTime(DateUtils.getNowDate());
        return deviceMapper.updateSmDeviceByIot(device);
    }

    /**
     * ids查询列表
     *
     * @param ids
     * @param userId
     */
    @Override
    public List<DeviceVO> selectByIds(List<Long> ids, Long userId) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return Collections.emptyList();
        }
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(ids);
        dto.setUserId(userId);
        return selectSmDeviceList(dto);
    }

    /**
     * 查询各店铺的设备数量,并店铺
     *
     * @param storeIds 店铺id
     */
    @Override
    public Map<Long, Integer> selectCountMapByStoreIds(List<Long> storeIds) {
        if (CollectionUtils.isEmptyElement(storeIds)) {
            return Collections.emptyMap();
        }
        List<DeviceCountVO> list = deviceMapper.selectCountByStoreIds(storeIds);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(DeviceCountVO::getStoreId, DeviceCountVO::getCount));
    }

    /**
     * 条件查询数量，并店铺
     *
     * @param query 查询条件
     * @param keyMapper 键值的映射
     */
    @Override
    public <K> Map<K, Integer> selectCommonCountMap(DeviceQuery query, Function<? super DeviceCountVO, ? extends K> keyMapper ) {
        if (query == null) {
            return Collections.emptyMap();
        }
        List<DeviceCountVO> list = deviceMapper.selectCommonCount(query);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(keyMapper, DeviceCountVO::getCount));
    }

    /**
     * 获取设备，包含套餐列表
     *
     * @param deviceSn
     * @return
     */
    @Override
    public DeviceVO getDeviceWithSuitList(String deviceSn) {
        if (deviceSn == null) {
            return null;
        }
        DeviceVO device = deviceMapper.selectBySn(deviceSn);
        if (device != null) {
            deviceAssembler.assembleSuitList(Collections.singletonList(device));
        }
        return device;
    }

    /**
     * 根据设备编号查询
     *
     * @param deviceNo
     */
    @Override
    public DeviceVO selectBySn(String deviceNo) {
        return deviceMapper.selectBySn(deviceNo);
    }

    @Override
    @Transactional
    public int unbind(Long deviceId) {
        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");
        // 判断是否有正在使用中的订单
//        ServiceUtil.assertion(deviceValidator.hasUsingBill(device.getDeviceId()), "当前设备有正在使用中的订单，请关闭后重试");

        Integer result = transactionTemplate.execute(status -> {
            int updateCount = deviceMapper.unbindStore(deviceId);
            ServiceUtil.assertion(updateCount != 1, "当前设备信息已变更，请刷新后重试");

            // 删除设备与设施的关系（投放状态）
            offlineDevice(deviceId, device.getPlacementType());

            // 新增解绑记录
            deviceBindRecordService.record(device.getUserId(), deviceId, BindRecordType.UNBIND, BindRecordUserType.MCH);

            return updateCount;
        });

        return result == null ? 0 : result;
    }

    /**
     * 设备绑定SN码，若其他设备已有该SN则直接替换
     *
     * @param deviceId 设备ID
     * @param sn       SN
     */
    @Override
    public int bindSn(Long deviceId, String sn) {
        if (deviceId == null || StringUtils.isBlank(sn)) {
            return 0;
        }
        return deviceMapper.bindSn(deviceId, sn);
    }

    @Override
    public int updateVxs(Long deviceId, BigDecimal vxs, String reason) {
        if (deviceId == null || vxs == null) {
            return 0;
        }
        DeviceVO device = this.selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");

        CommandResponse res = iotService.setVxs(device, vxs, reason);
        return res != null && res.isSuccess() ? 1 : 0;
    }

    @Override
    public int uploadData(DeviceVO device, String reason) {
        ServiceUtil.assertion(device == null, "设备不存在");
        CommandResponse res = iotService.uploadData(device, reason, true);
        return res != null && res.isSuccess() ? 1 : 0;
    }

    /**
     * 通用设备开关控制方法
     * @param deviceId 设备ID
     * @param unlockTime 开锁时间（0表示立马开启/关闭）
     * @param unlockMode 开锁模式（1-通电开锁，2-断电开锁）
     * @return 操作是否成功
     */
    @Override
    public boolean controlDevice(Long deviceId, Integer unlockTime, String unlockMode, boolean open, boolean isIgnore, String reason, boolean returnMac) {
        // 参数校验
        validateParams(deviceId, unlockTime, unlockMode);

        // 获取设备信息
        DeviceVO deviceVO = selectById(deviceId);
        ServiceUtil.assertion(deviceVO == null, "设备不存在");

        return unlockTime == 0 ?
                handleDirectControl(deviceId, unlockMode, open, isIgnore, reason,returnMac) :
                handleTimedControl(deviceVO, unlockTime, unlockMode,reason);
    }

    /**
     * 通用控制门方法
     * @param deviceId 设备ID
     * @param unlockTime 开锁时间（0表示立马开启/关闭）
     * @param unlockMode 开锁模式（1-通电开锁，2-断电开锁）
     * @param open 开/关
     * @param reason 原因
     * @param returnMac 是否返回mac地址
     * @return 操作是否成功
     */
    @Override
    public boolean controlDoor(Long deviceId, Integer unlockTime, String unlockMode, boolean open, String reason, boolean returnMac) {
        // 参数校验
        validateDoorParams(deviceId, unlockTime, unlockMode);

        // 获取设备信息
        DeviceVO deviceVO = selectById(deviceId);
        ServiceUtil.assertion(deviceVO == null, "设备不存在");

        return handledDoorControl(deviceVO, unlockTime, unlockMode,reason,returnMac);
    }

    /**
     * 参数校验
     */
    private void validateParams(Long deviceId, Integer unlockTime, String unlockMode) {
        ServiceUtil.assertion(deviceId == null, "设备ID不能为空");
        ServiceUtil.assertion(unlockTime == null, "开锁时间不能为空");
        ServiceUtil.assertion((!LOCK_MODE_POWER_ON.equals(unlockMode) && !LOCK_MODE_POWER_OFF.equals(unlockMode)),"无效的开锁模式");
    }

    /**
     * 门参数校验
     */
    private void validateDoorParams(Long deviceId, Integer unlockTime, String unlockMode) {
        ServiceUtil.assertion(deviceId == null, "设备ID不能为空");
        ServiceUtil.assertion(unlockTime == null, "开锁时间不能为空");
        ServiceUtil.assertion(unlockTime < 1, "开锁时间不能小于1秒");
        ServiceUtil.assertion((!LOCK_MODE_POWER_ON.equals(unlockMode) && !LOCK_MODE_POWER_OFF.equals(unlockMode)),"无效的开锁模式");
    }

    /**
     * 处理直接控制
     */
    private boolean handleDirectControl(Long deviceId, String unlockMode, boolean open,  boolean isIgnore, String reason,boolean returnMac) {
        if(!LOCK_MODE_POWER_ON.equals(unlockMode)){
            open = !open;
        }
        return switchDevice(deviceId, open, isIgnore,reason, returnMac);
    }

    /**
     * 处理定时控制
     */
    private boolean handleTimedControl(DeviceVO deviceVO, Integer unlockTime, String unlockMode,String reason) {
        int result;
        if (LOCK_MODE_POWER_ON.equals(unlockMode)) {
            result = setTtme(deviceVO, unlockTime.longValue(),reason);
            ServiceUtil.assertion(result == 0, "通电开锁失败");
        } else {
            result = setTtme(deviceVO, unlockTime.longValue(),reason);
            ServiceUtil.assertion(result == 0, "断电开锁失败");
        }
        return true;
    }

    /**
     * 门控制方法
     */
    private boolean handledDoorControl(DeviceVO deviceVO, Integer unlockTime, String unlockMode,String reason, boolean returnMac) {
        int result;
        if (LOCK_MODE_POWER_ON.equals(unlockMode)) {
            result = km(deviceVO, unlockTime.longValue(),reason,returnMac);
            ServiceUtil.assertion(result == 0, "通电开锁失败");
        } else {
            result = gm(deviceVO, unlockTime.longValue(),reason,returnMac);
            ServiceUtil.assertion(result == 0, "断电开锁失败");
        }
        return true;
    }
}
