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

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.github.pagehelper.PageHelper;
import com.ruoyi.bst.bindRecord.domain.BindRecord;
import com.ruoyi.bst.bindRecord.domain.enums.BindRecordType;
import com.ruoyi.bst.bindRecord.service.BindRecordService;
import com.ruoyi.bst.booth.domain.Booth;
import com.ruoyi.bst.booth.domain.BoothQuery;
import com.ruoyi.bst.booth.domain.BoothVO;
import com.ruoyi.bst.booth.mapper.BoothMapper;
import com.ruoyi.bst.booth.service.BoothService;
import com.ruoyi.bst.booth.service.BoothValidator;
import com.ruoyi.bst.device.domain.DeviceCountVO;
import com.ruoyi.bst.device.domain.dto.DeviceWifiDTO;
import com.ruoyi.bst.device.domain.enums.DeviceOnlineStatus;
import com.ruoyi.bst.device.domain.enums.DevicePowerStatus;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.model.domain.ModelVO;
import com.ruoyi.bst.model.domain.enums.ModelTag;
import com.ruoyi.bst.model.service.ModelService;
import com.ruoyi.bst.store.domain.Store;
import com.ruoyi.bst.store.domain.StoreVO;
import com.ruoyi.bst.store.service.StoreService;
import com.ruoyi.bst.storeStaff.domain.enums.StoreStaffPermission;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.domain.vo.LongIntegerVO;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.domain.IotDeviceInfo;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.enums.IotHttpStatus;
import com.ruoyi.iot.service.IotService;
import com.ruoyi.system.user.service.UserService;
import com.sun.xml.bind.v2.TODO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.device.mapper.DeviceMapper;
import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.service.DeviceService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * 设备Service业务层处理
 *
 * @author ruoyi
 * @date 2025-04-23
 */
@Service
public class DeviceServiceImpl implements DeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private ModelService modelService;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private UserService userService;
    @Autowired
    private StoreService storeService;
    @Autowired
    private BindRecordService bindRecordService;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private IotService iotService;
    @Autowired
    private BoothService boothService;
    @Autowired
    private BoothValidator boothValidator;

    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public DeviceVO selectDeviceByDeviceId(Long id, boolean scope)
    {
        if (id == null) {
            return null;
        }
        DeviceQuery query = new DeviceQuery();
        query.setBoothId(id);
        query.setScope(scope);
        query.addStorePermission(StoreStaffPermission.BOOTH_VIEW.getCode());
        return this.selectOne(query);
    }

    /**
     * 查询一个
     * @param query
     * @return
     */
    @Override
    public DeviceVO selectOne(DeviceQuery query) {
        PageHelper.startPage(1, 1);
        List<DeviceVO> list = this.selectDeviceList(query);
        return CollectionUtils.firstElement(list);
    }
    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<DeviceVO> selectDeviceList(DeviceQuery device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        String key = device.getDeviceNo();
        ServiceUtil.assertion(!redisLock.lock(RedisLockKey.ADD_DEVICE, key), "当前新增设备过于频繁，请稍后再试");
        try{
            ModelVO model = modelService.selectModelByModelId(device.getModelId());
            ServiceUtil.assertion(model==null,"当前型号不存在");

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

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

                // 后校验
                DeviceVO vo = this.selectDeviceByDeviceId(device.getDeviceId());
                checkUnique(vo);

                // 注册到OneNet
                this.registerToOneNet(vo);
                return insert;

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

    public void checkUnique(Device device){
        if (device.getMac2()!=null){
            ServiceUtil.assertion(device.getMac2().equals(device.getMac()),"MAC-1与MAC-2不允许重复");
        }
        int count = deviceMapper.checkUnique(device);
        ServiceUtil.assertion(count>0,"当前设备已存在");
    }

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

    /**
     * 修改设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        device.setUpdateTime(DateUtils.getNowDate());
        Integer result = transactionTemplate.execute(status -> {

            if (device.getModelId()!=null){
                ModelVO model = modelService.selectModelByModelId(device.getModelId());
                ServiceUtil.assertion(model==null,"当前型号不存在");
            }


            // 更新主表
            int update = deviceMapper.updateDevice(device);
            ServiceUtil.assertion(update != 1, "更新设备失败");

            // 后校验
            DeviceVO vo = this.selectDeviceByDeviceId(device.getDeviceId());
            ServiceUtil.assertion(vo==null,"当前设备不存在");
            checkUnique(vo);

            // TODO :能否更改MAC号？
            //ServiceUtil.assertion(device.getMac()!=null || device.getMac2()!=null,"当前设备不允许修改MAC号");
            // 若修改了MAC或者型号，则需要重新注册到OneNet
            if (device.getMac() != null || device.getMac2() != null || device.getModelId() != null) {
                this.registerToOneNet(vo);
            }

            return update;
        });
        return result == null ? 0 : result;
    }

    /**
     * 批量删除设备
     *
     * @param deviceIds 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByDeviceIds(Long[] deviceIds)
    {
        this.validatePreDelete(Arrays.asList(deviceIds));
        return deviceMapper.deleteDeviceByDeviceIds(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));
        }
    }


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


    /**
     * 删除设备信息
     *
     * @param deviceId 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByDeviceId(Long deviceId)
    {
        return deviceMapper.deleteDeviceByDeviceId(deviceId);
    }

    @Override
    public int logicDel(List<Long> ids) {
        return deviceMapper.logicDel(ids);
    }

    @Override
    public String bindMchById(Long deviceId, Long userId) {
        DeviceVO device = this.selectDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null, "设备未录入");
        return bindMch(device, userId);
    }


    @Override
    public String bindMchBySn(String deviceNo, Long userId) {
        DeviceVO device = this.selectByDeviceNo(deviceNo);
        ServiceUtil.assertion(device == null, "设备未录入");
        return bindMch(device,userId);
    }

    public String bindMch(Device device, Long userId) {
        // 查询用户
        UserVO user = userService.selectSimpleById(userId);
        ServiceUtil.assertion(user == null, "用户不存在");

        // 检查是否已绑定相同的设备
        if (device.getUserId() != null) {
            if (Objects.equals(device.getUserId(), userId)) {
                return device.getMac();
            } else {
                throw new ServiceException("该设备已被绑定");
            }
        }

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

            // 记录绑定记录
            int record = bindRecordService.record(userId, device.getDeviceId(), BindRecordType.BIND);
            ServiceUtil.assertion(record != 1, "添加绑定记录失败");
            return updateCount;
        });

        return device.getMac();
    }

    @Override
    public int unbind(Long deviceId) {
        DeviceVO device = deviceMapper.selectDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");

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

            // 新增解绑记录
            bindRecordService.record(device.getUserId(), deviceId, BindRecordType.UNBIND);
            return updateCount;
        });

        return result == null ? 0 : result;
    }

    @Override
    public DeviceVO selectByDeviceNo(String deviceNo) {
        return deviceMapper.selectByDeviceNo(deviceNo);
    }
    @Override
    public int updateOnlineStatusByMac(String mac, String status) {
        if (StringUtils.isAnyBlank(mac, status)) {
            return 0;
        }
        return deviceMapper.updateOnlineStatusByMac(mac, status);
    }

    private List<Long> selectIds(DeviceQuery query) {
        return deviceMapper.selectIds(query);
    }



    @Override
    public String bindBoothById(Long deviceId, BoothVO booth) {
        DeviceVO device = selectDeviceByDeviceId(deviceId);
        return this.bindBooth(booth,device);
    }

    @Override
    public String bindBoothBySN(String deviceNo, BoothVO booth) {
        DeviceVO device = selectByDeviceNo(deviceNo);
        return this.bindBooth(booth,device);
    }

    @Override
    public void unbindBooth(Long deviceId, BoothVO booth) {
        DeviceVO device = deviceMapper.selectDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null, "设备未录入");

        // 判断当前设备是否被绑定
        ServiceUtil.assertion(device.getStoreId()==null,"请先为设备绑定店铺");
        ServiceUtil.assertion(device.getUserId()==null,"请先为设备绑定所属用户");

        // 解绑
        int i = deviceMapper.unbindBooth(device.getDeviceId());
        ServiceUtil.assertion(i==0,"绑定失败");
    }

    private String bindBooth(BoothVO booth,  DeviceVO device) {
        ServiceUtil.assertion(device == null, "设备未录入");

        // 检查是否已绑定相同的卡座
        if (device.getBoothId() != null) {
            if (Objects.equals(device.getBoothId(), booth.getBoothId())) {
                return device.getMac();
            } else {
                throw new ServiceException("该设备已被绑定");
            }
        }

        // 绑定
        int i = deviceMapper.bindBooth(device.getDeviceId(),device.getDeviceNo(),booth);
        ServiceUtil.assertion(i==0,"绑定失败");
        return device.getMac();
    }


    /**
     * 设置数据库设备时长
     *
     * @param deviceId 设备ID
     * @param duration  秒
     * @return
     */
    @Override
    public int setTimeDb(Long deviceId, Long duration) {
        if (deviceId == null || duration <= 0) {
            return 0;
        }
        return deviceMapper.setTime(deviceId, duration);
    }


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

    @Override
    public int updateDeviceStore(Device device) {
        return deviceMapper.changeStore(device);
    }

    @Override
    public void unbindStore(Long deviceId, StoreVO store) {
        DeviceVO device = deviceMapper.selectDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null, "设备未录入");

        // 判断当前设备是否被绑定
        ServiceUtil.assertion(device.getUserId()==null,"请先为设备绑定所属用户");

        // 解绑
        int i = deviceMapper.unbindStore(device.getDeviceId());
        ServiceUtil.assertion(i==0,"绑定失败");
    }
}