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

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceLockStatus;
import com.ruoyi.bst.device.domain.enums.DeviceQuality;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.device.domain.enums.DeviceUnLockType;
import com.ruoyi.bst.device.domain.vo.DeviceIotVO;
import com.ruoyi.bst.device.mapper.DeviceMapper;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.utils.DeviceUtil;
import com.ruoyi.bst.orderDevice.domain.enums.OrderDeviceStatus;
import com.ruoyi.common.constants.ServiceCode;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.constants.IotConstants;
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.iot.service.impl.DeviceOnlineStatus;
import com.ruoyi.iot.util.IotUtil;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class DeviceIotServiceImpl implements DeviceIotService {

    @Autowired
    private IotService iotService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceService deviceService;
    
    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private RedisLock redisLock;

    private final static Integer SUB_FAST = 5;    // 上报频率（快）
    private final static Integer SUB_SLOW = 300;   // 上报频率（慢）

    @Override
    public DeviceIotVO unlock(DeviceVO device, DeviceUnLockType type, String reason, boolean requiredIot) {
        if (device == null || device.getId() == null) {
            return new DeviceIotVO(0, false);
        }
        DeviceIotVO vo = new DeviceIotVO();

        // 更新设备状态
        Device data = new Device();
        data.setLockStatus(DeviceLockStatus.OPEN.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());

        // 管理员解锁
        if (DeviceUnLockType.ADMIN.equals(type)) {
            ServiceUtil.assertion(!DeviceStatus.canAdminUnlock().contains(device.getStatus()), "设备%s当前状态不允许管理员开锁", device.getSn());
            data.setStatus(DeviceStatus.DISPATCHING.getCode());
            query.setStatusList(DeviceStatus.canAdminUnlock());
        }
        // 用户解锁
        else if (DeviceUnLockType.USER.equals(type)) {
            ServiceUtil.assertion(!DeviceStatus.canUserUnlock().contains(device.getStatus()), "设备%s当前状态不允许用户开锁", device.getSn());
            data.setStatus(DeviceStatus.IN_USE.getCode());
            query.setStatusList(DeviceStatus.canUserUnlock());
        }
        // 返回运营区上电
        else if (DeviceUnLockType.BACK_AREA.equals(type)) {
            ServiceUtil.assertion(!DeviceStatus.canBackUnlock().contains(device.getStatus()), "设备%s当前状态不允许返回运营区开锁", device.getSn());
            data.setStatus(DeviceStatus.IN_USE.getCode());
            query.setStatusList(DeviceStatus.canBackUnlock());
        }

        Integer result = transactionTemplate.execute(status -> {
            // 更新数据库
            int rows = deviceMapper.updateByQuery(data, query);
            vo.setDb(rows);

            if (rows > 0) {
                // 发送命令开锁
                CommandResponse res = iotService.unlock(device, SUB_FAST, reason);
                boolean iot = IotUtil.isSuccess(res);
                ServiceUtil.assertion(requiredIot && !iot, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
                vo.setIot(iot);
            }

            return rows;
        });

        // 异步执行3次设备监控
        if (!requiredIot) {
            this.asyncMonitorCount(device.getId(), 3);
        }

        return vo;
    }

    @Override
    public DeviceIotVO lock(DeviceVO device, boolean isAdmin, String reason, boolean requiredIot) {
        if (device == null || device.getId() == null) {
            return new DeviceIotVO(0, false);
        }
        DeviceIotVO vo = new DeviceIotVO();

        // 是否有正在进行的订单
        boolean hasOrder = device.getOrderDeviceId() != null && OrderDeviceStatus.inUse().contains(device.getOrderDeviceStatus());

        // 构造数据
        Device data = new Device();
        data.setLockStatus(DeviceLockStatus.CLOSE.getCode());
        data.setStatus(hasOrder ? DeviceStatus.TEMP_LOCKED.getCode() : DeviceStatus.AVAILABLE.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        if (isAdmin) {
            ServiceUtil.assertion(!DeviceStatus.canAdminLock().contains(device.getStatus()), "设备%s当前状态不允许管理员锁车", device.getSn());
            query.setStatusList(DeviceStatus.canAdminLock());
        } else {
            ServiceUtil.assertion(!DeviceStatus.canUserLock().contains(device.getStatus()), "设备%s当前状态不允许用户锁车", device.getSn());
            query.setStatusList(DeviceStatus.canUserLock());
        }

        Integer result = transactionTemplate.execute(status -> {
            // 更新设备状态
            int rows = deviceMapper.updateByQuery(data, query);
            vo.setDb(rows);

            if (rows > 0) {
                // 发送命令锁车
                CommandResponse res = null;
                if (DeviceStatus.TEMP_LOCKED.getCode().equals(data.getStatus())) {
                    // 临时锁车
                    res = iotService.tempLock(device, SUB_SLOW, reason);
                } else {
                    // 锁车
                    res = iotService.lock(device, SUB_SLOW, reason);
                }
                boolean iot = IotUtil.isSuccess(res);
                ServiceUtil.assertion(requiredIot && !iot, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
                vo.setIot(iot);
            }

            return rows;
        });

        // 异步执行3次设备监控
        if (!requiredIot) {
            this.asyncMonitorCount(device.getId(), 3);
        }

        return vo;
    }

    @Override
    public DeviceIotVO qLock(DeviceVO device, String reason, boolean requiredIot) {
        if (device == null || device.getId() == null) {
            return new DeviceIotVO(0, false);
        }
        ServiceUtil.assertion(!DeviceStatus.canQLock().contains(device.getStatus()), "设备%s当前状态不允许强制锁车", device.getSn());

        DeviceIotVO vo = new DeviceIotVO();

        // 是否有正在进行的订单
        boolean hasOrder = device.getOrderDeviceId() != null && OrderDeviceStatus.inUse().contains(device.getOrderDeviceStatus());

        Integer result = transactionTemplate.execute(status -> {
            // 更新设备状态
            Device data = new Device();
            data.setLockStatus(DeviceLockStatus.CLOSE.getCode());
            data.setStatus(hasOrder ? DeviceStatus.Q_LOCKED.getCode() : DeviceStatus.AVAILABLE.getCode());
            DeviceQuery query = new DeviceQuery();
            query.setId(device.getId());
            query.setStatusList(DeviceStatus.canQLock());
            int rows = deviceMapper.updateByQuery(data, query);
            vo.setDb(rows);

            if (rows > 0) {
                // 发送命令锁车
                CommandResponse res = iotService.qLock(device, SUB_FAST, reason);
                boolean iot = IotUtil.isSuccess(res);
                ServiceUtil.assertion(requiredIot && !iot, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
                vo.setIot(iot);
            }

            return rows;
        });

        // 异步执行3次设备监控
        if (!requiredIot) {
            this.asyncMonitorCount(device.getId(), 3);
        }

        return vo;
    }

    @Override
    public boolean play(DeviceVO device, String type, String reason, boolean requiredIot) {
        if (device == null) {
            return false;
        }
        CommandResponse res = iotService.play(device, type, reason);
        boolean success = IotUtil.isSuccess(res);
        ServiceUtil.assertion(requiredIot && !success, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
        return success;
    }

    @Override
    public boolean reboot(DeviceVO device, String reason, boolean requiredIot) {
        if (device == null) {
            return false;
        }
        CommandResponse res = iotService.reboot(device, reason);
        boolean success = IotUtil.isSuccess(res);
        ServiceUtil.assertion(requiredIot && !success, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
        return success;
    }

    @Override
    public boolean unlockSeat(DeviceVO device, String reason, boolean requiredIot) {
        if (device == null) {
            return false;
        }
        CommandResponse res = iotService.unlockSeat(device, reason);
        boolean success = IotUtil.isSuccess(res);
        ServiceUtil.assertion(requiredIot && !success, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
        return success;
    }

    @Override
    public void refresh(List<DeviceVO> deviceList, String onlineType) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return;
        }

        // 获取物联网信息
        List<IotDeviceInfo> iotList = iotService.getDeviceInfo(deviceList);
        if (CollectionUtils.isEmptyElement(iotList)) {
            return;
        }

        // 更新设备信息
        for (DeviceVO device : deviceList) {
            if (device == null || StringUtils.isBlank(device.getMac())) {
                continue;
            }
            IotDeviceInfo iot = iotList.stream()
                    .filter(item -> Objects.equals(item.getMac(), device.getMac()))
                    .findFirst().orElse(null);
            DeviceUtil.setIotInfo(device, iot);
        }

        // 异步发送命令，强制设备上报数据
        scheduledExecutorService.schedule(() -> {
            for (DeviceVO device : deviceList) {
                try {
                    if (onlineType != null) {
                        iotService.getOnlineStatus(device, onlineType);
                    }
                } catch (Exception e) {
                    log.error("强制设备{}上报数据失败", device.getSn(), e);
                }
            }
        }, 0, TimeUnit.SECONDS);
    }

    private void setOnlineStatus(DeviceVO device, String onlineStatus) {
        // 是否在线
        device.setOnlineStatus(onlineStatus);
        if (DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus)) {
            device.setLastOnlineTime(LocalDateTime.now());
        }
    }

    public int updateIot(Device device) {
        if (device == null) {
            return 0;
        }
        if (StringUtils.isBlank(device.getMac()) && device.getId() == null) {
            return 0;
        }
        Device data = new Device();
        data.setVoltage(device.getVoltage());
        data.setSignalStrength(device.getSignalStrength());
        data.setQuality(device.getQuality());
        data.setSatellites(device.getSatellites());
        data.setIotStatus(device.getIotStatus());
        data.setLongitude(device.getLongitude());
        data.setLatitude(device.getLatitude());
        data.setRemainingPower(device.getRemainingPower());
        data.setLastTime(device.getLastTime());
        data.setLastLocationTime(device.getLastLocationTime());
        data.setOnlineStatus(device.getOnlineStatus());
        data.setLastOnlineTime(device.getLastOnlineTime());
        data.setSoftwareVersion(device.getSoftwareVersion());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        query.setEqMac(device.getMac());
        return deviceMapper.updateByQuerySimple(data, query);
    }

    @Override
    public boolean create(String mac) {
        if (StringUtils.isBlank(mac)) {
            return false;
        }
        int code = iotService.create(mac);
        IotHttpStatus status = IotHttpStatus.convertByCode(code);
        ServiceUtil.assertion(!status.equals(IotHttpStatus.SUCCESS) && !status.equals(IotHttpStatus.DEVICE_EXIST),
             "设备【%s】注册失败:%s", mac, status.getMsg());
        return true;
    }

    @Override
    public DeviceIotVO lock(Long id, boolean isAdmin, String reason, boolean requiredIot) {
        DeviceVO device = deviceMapper.selectDeviceById(id);
        return this.lock(device, isAdmin, reason, requiredIot);
    }

    @Override
    public DeviceIotVO unlock(Long id, DeviceUnLockType type, String reason, boolean requiredIot) {
        DeviceVO device = deviceMapper.selectDeviceById(id);
        return this.unlock(device, type, reason, requiredIot);
    }

    @Override
    public void monitor(List<Long> ids) {
        DeviceQuery query = new DeviceQuery();
        query.setIds(ids);
        List<Long> idList = deviceMapper.selectIdList(query);
        if (CollectionUtils.isEmptyElement(idList)) {
            return;
        }

        // 监控设备
        for (Long deviceId : idList) {
            try {
                scheduledExecutorService.schedule(() -> {
                    // 获取设备最新信息
                    DeviceVO device = deviceMapper.selectDeviceById(deviceId);
                    this.refresh(device, null);
                    this.monitor(device);
                },0, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("监控设备id={}出错：{}", deviceId, e.getMessage());
            }
        }
    }

    private void monitor(DeviceVO device) {
        if (device == null) {
            return;
        }
        // 判断电门与设备锁状态是否一致，不一致则发送命令
        if (DeviceLockStatus.OPEN.getCode().equals(device.getLockStatus()) && !DeviceQuality.OPEN.getCode().equals(device.getQuality())) {
            iotService.unlock(device, SUB_FAST, "【设备监控】电门异常未开启");
        }
        if (DeviceLockStatus.CLOSE.getCode().equals(device.getLockStatus()) && !DeviceQuality.CLOSE.getCode().equals(device.getQuality())) {
            iotService.lock(device, SUB_SLOW, "【设备监控】电门异常未关闭");
        }
    }

    @Override
    public void monitor(String mac) {
        DeviceVO device = deviceService.selectByMac(mac);
        this.refresh(device, null);
        this.monitor(device);
    }

    private void asyncMonitorCount(Long deviceId, int count) {
        scheduledExecutorService.schedule(() -> {
            if (count > 0) {
                this.monitor(Collections.singletonList(deviceId));
                this.asyncMonitorCount(deviceId, count - 1);
            }
        }, count, TimeUnit.SECONDS);
    }

    @Override
    public DeviceIotVO setMusic(DeviceVO device, String music, String reason, boolean requiredIot) {
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(StringUtils.isBlank(music), "声音设置不能为空");

        String softwareVersion = device.getSoftwareVersion();
        ServiceUtil.assertion(softwareVersion == null, "当前设备暂无软件版本，无法设置声音");
        if (IotConstants.MUSIC_VOICE.equals(music)) {
            ServiceUtil.assertion(MathUtils.smallerThan(softwareVersion, "6000"), "当前设备软件版本低于6000，无法设置为语音");
        }
        if (IotConstants.MUSIC_RING.equals(music)) {
            ServiceUtil.assertion(MathUtils.smallerThan(softwareVersion, "6900"), "当前设备软件版本低于6900，无法设置为彩铃");
        }
        
        DeviceIotVO vo = new DeviceIotVO();

        transactionTemplate.execute(status -> {
            Device data = new Device();
            data.setId(device.getId());
            data.setMusic(music);
            int rows = deviceMapper.updateDevice(data);
            vo.setDb(rows);

            if (rows > 0) {
                CommandResponse res = iotService.setMusic(device, music, reason);
                boolean success = IotUtil.isSuccess(res);
                ServiceUtil.assertion(requiredIot && !success, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
                vo.setIot(success);
            }

            return rows;
        });

        return vo;
    }
}
