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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

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

import com.github.pagehelper.PageHelper;
import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission;
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.DeviceStatus;
import com.ruoyi.bst.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.bst.device.mapper.DeviceMapper;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.service.DeviceValidator;
import com.ruoyi.bst.device.utils.DeviceUtil;
import com.ruoyi.bst.model.service.ModelValidator;
import com.ruoyi.bst.orderDevice.domain.enums.OrderDeviceStatus;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
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 lombok.extern.slf4j.Slf4j;

/**
 * 设备Service业务层处理
 *
 * @author ruoyi
 * @date 2025-03-20
 */
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService {
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceIotService deviceIotService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ModelValidator modelValidator;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private DeviceValidator deviceValidator;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private DeviceAssembler deviceAssembler;

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

    @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 query) {
        List<DeviceVO> list = deviceMapper.selectDeviceList(query);
        deviceAssembler.assembleIot(list);
        return list;
    }

    @Override
    public List<DeviceVO> selectDeviceByIds(List<Long> ids, boolean scope, AreaJoinPermission permission) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return Collections.emptyList();
        }
        DeviceQuery query = new DeviceQuery();
        query.setScope(scope);
        query.setIds(ids);
        query.addAreaPermission(permission.getCode());
        return this.selectDeviceList(query);
    }

    /**
     * 执行带有SN和MAC锁的设备操作
     *
     * @param device    设备信息
     * @param operation 具体操作的函数
     * @return 操作结果
     */
    private int executeWithLock(Device device, Function<Device, Integer> operation) {
        if (StringUtils.hasText(device.getSn())) {
            boolean lockSn = redisLock.lock(RedisLockKey.DEVICE_SN_UNIQUE, device.getSn());
            ServiceUtil.assertion(!lockSn, "SN为%s的设备当前操作的人过多，请稍后再试", device.getSn());
        }
        try {
            if (StringUtils.hasText(device.getMac())) {
                boolean lockMac = redisLock.lock(RedisLockKey.DEVICE_MAC_UNIQUE, device.getMac());
                ServiceUtil.assertion(!lockMac, "MAC为%s的设备当前操作的人过多，请稍后再试", device.getMac());
            }
            try {
                Integer result = operation.apply(device);
                return result == null ? 0 : result;
            } finally {
                if (StringUtils.hasText(device.getMac())) {
                    redisLock.unlock(RedisLockKey.DEVICE_MAC_UNIQUE, device.getMac());
                }
            }
        } finally {
            if (StringUtils.hasText(device.getSn())) {
                redisLock.unlock(RedisLockKey.DEVICE_SN_UNIQUE, device.getSn());
            }
        }
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        device.setCreateTime(DateUtils.getNowDate());
        return executeWithLock(device, dev -> transactionTemplate.execute(status -> {
            int row = deviceMapper.insertDevice(dev);
            if (row > 0) {
                // 查询设备
                DeviceVO vo = selectDeviceById(dev.getId());

                // 校验数据
                deviceValidator.validate(vo);

                // 注册OneNet设备
                deviceIotService.create(dev.getMac());

                // 若车型不是用户的，则将设备上的车型清空
                boolean reset = this.resetModel(dev);
                ServiceUtil.assertion(!reset, "设备%s的车型清空失败", dev.getSn());

                // 清除缓存
                this.clearCache();
            }
            return row;
        }));
    }

    /**
     * 若车型不是用户的，则将设备上的车型清空
     *
     * @param device
     */
    private boolean resetModel(Device device) {
        if (device.getModelId() != null && device.getMchId() != null
                && !modelValidator.isUser(device.getModelId(), device.getMchId())) {
            int rows = deviceMapper.resetModel(device.getId());
            return rows == 1;
        }
        return true;
    }

    /**
     * 修改设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device) {
        device.setUpdateTime(DateUtils.getNowDate());
        return executeWithLock(device, dev -> transactionTemplate.execute(status -> {
            int row = deviceMapper.updateDevice(dev);
            if (row > 0 && StringUtils.hasText(dev.getMac())) {
                // 查询设备
                DeviceVO vo = selectDeviceById(dev.getId());

                // 校验数据
                deviceValidator.validate(vo);

                // 注册OneNet设备
                deviceIotService.create(dev.getMac());

                // 若车型不是用户的，则将设备上的车型清空
                boolean reset = this.resetModel(vo);
                ServiceUtil.assertion(!reset, "设备%s的车型清空失败", vo.getSn());

                // 清除缓存
                this.clearCache();
            }
            return row;
        }));
    }

    @Override
    public int logicDel(List<Long> ids) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return 0;
        }

        // 校验
        List<DeviceVO> deviceList = this.selectDeviceByIds(ids);
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        for (DeviceVO device : deviceList) {
            ServiceUtil.assertion(DeviceUtil.isOrderUsing(device), "设备%s正在使用，不能删除", device.getSn());
        }

        int rows = deviceMapper.logicDel(ids);

        // 清除缓存
        if (rows > 0) {
            this.clearCache();
        }

        return rows;
    }

    @Override
    public List<DeviceVO> selectDeviceByIds(List<Long> ids, boolean scope) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return Collections.emptyList();
        }
        DeviceQuery query = new DeviceQuery();
        query.setIds(ids);
        query.setScope(scope);
        return deviceMapper.selectDeviceList(query);
    }

    @Override
    public int in(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        int rows = 0;
        for (DeviceVO device : deviceList) {
            if (device == null) {
                continue;
            }
            try {
                int row = this.in(device);
                ServiceUtil.assertion(row != 1, "设备%s入仓失败，请刷新后重试", device.getSn());
                rows += row;
            } catch (Exception e) {
                log.error("入仓ID为" + device.getId() + "的设备失败", e);
                throw e;
            }
        }
        return rows;
    }

    // 入仓
    private int in(DeviceVO device) {
        if (DeviceStatus.STORAGE.getCode().equals(device.getStatus())) {
            return 1;
        }
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
        ServiceUtil.assertion(!DeviceStatus.canIn().contains(device.getStatus()), "设备%s当前状态不允许入仓", device.getSn());

        // 更新设备状态
        Device data = new Device();
        data.setStatus(DeviceStatus.STORAGE.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        query.setStatusList(DeviceStatus.canIn());
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public int out(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        int rows = 0;
        for (DeviceVO device : deviceList) {
            if (device == null) {
                continue;
            }
            try {
                int row = this.out(device);
                ServiceUtil.assertion(row != 1, "设备%s出仓失败，请刷新后重试", device.getSn());
                rows += row;
            } catch (Exception e) {
                log.error("出仓ID为" + device.getId() + "的设备失败", e);
                throw e;
            }
        }
        return rows;
    }

    // 出仓
    private int out(DeviceVO device) {
        if (DeviceStatus.AVAILABLE.getCode().equals(device.getStatus())) {
            return 1;
        }
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
        ServiceUtil.assertion(!DeviceStatus.canOut().contains(device.getStatus()), "设备%s当前状态不允许出仓", device.getSn());

        // 更新设备状态
        Device data = new Device();
        data.setStatus(DeviceStatus.AVAILABLE.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        query.setStatusList(DeviceStatus.canOut());
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public int disable(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        int rows = 0;
        for (DeviceVO device : deviceList) {
            if (device == null) {
                continue;
            }
            try {
                int row = this.disable(device);
                ServiceUtil.assertion(row != 1, "设备%s禁用失败，请刷新后重试", device.getSn());
                rows += row;
            } catch (Exception e) {
                log.error("禁用ID为" + device.getId() + "的设备失败", e);
                throw e;
            }
        }
        return rows;
    }

    // 禁用
    private int disable(DeviceVO device) {
        if (DeviceStatus.DISABLED.getCode().equals(device.getStatus())) {
            return 1;
        }
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
        ServiceUtil.assertion(!DeviceStatus.canDisable().contains(device.getStatus()), "设备%s当前状态不允许禁用", device.getSn());

        // 更新设备状态
        Device data = new Device();
        data.setStatus(DeviceStatus.DISABLED.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        query.setStatusList(DeviceStatus.canDisable());
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public int enable(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        int rows = 0;
        for (DeviceVO device : deviceList) {
            if (device == null) {
                continue;
            }
            try {
                int row = this.enable(device);
                ServiceUtil.assertion(row != 1, "设备%s禁用失败，请刷新后重试", device.getSn());
                rows += row;
            } catch (Exception e) {
                log.error("禁用ID为" + device.getId() + "的设备失败", e);
                throw e;
            }
        }
        return rows;
    }

    // 启用
    private int enable(DeviceVO device) {
        if (DeviceStatus.AVAILABLE.getCode().equals(device.getStatus())) {
            return 1;
        }
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
        ServiceUtil.assertion(!DeviceStatus.canEnable().contains(device.getStatus()), "设备%s当前状态不允许启用", device.getSn());

        // 更新设备状态
        Device data = new Device();
        data.setStatus(DeviceStatus.AVAILABLE.getCode());
        DeviceQuery query = new DeviceQuery();
        query.setId(device.getId());
        query.setStatusList(DeviceStatus.canEnable());
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public DeviceVO selectDeviceBySn(String sn, boolean scope) {
        if (StringUtils.isBlank(sn)) {
            return null;
        }
        DeviceQuery query = new DeviceQuery();
        query.setEqSn(sn);
        query.setScope(scope);
        query.addAreaPermission(AreaJoinPermission.DEVICE_VIEW.getCode());
        return this.selectOne(query);
    }

    @Override
    public DeviceVO selectSimpleByMacForLocationLog(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        }
        DeviceQuery query = new DeviceQuery();
        query.setEqMac(mac);
        return deviceMapper.selectSimpleForLocationLog(query);
    }

    @Override
    public int updateCurrentDeviceOrderId(Long id, Long deviceOrderId) {
        if (id == null || deviceOrderId == null) {
            return 0;
        }
        // 允许下单的订单设备状态
        List<String> statusList = Arrays.asList(
                OrderDeviceStatus.WAIT_USE.getCode(),
                OrderDeviceStatus.FINISHED.getCode());

        Device data = new Device();
        data.setOrderDeviceId(deviceOrderId);
        DeviceQuery query = new DeviceQuery();
        query.setId(id);
        query.setNonOrOrderDeviceStatusList(statusList);
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public int clearCurrentOrderDevice(Long id, Long orderDeviceId) {
        if (id == null || orderDeviceId == null) {
            return 0;
        }
        return deviceMapper.clearCurrentOrderDevice(id, orderDeviceId);
    }

    @Override
    public int transfer(List<DeviceVO> deviceList, Long areaId, Long mchId) {
        if (CollectionUtils.isEmptyElement(deviceList) || areaId == null) {
            return 0;
        }
        // 校验
        for (DeviceVO device : deviceList) {
            ServiceUtil.assertion(device == null, "设备不能为空");
            ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
            ServiceUtil.assertion(DeviceUtil.isOrderUsing(device), "设备%s正在使用，不能划拨", device.getSn());
        }

        List<Long> ids = CollectionUtils.map(deviceList, DeviceVO::getId);
        if (CollectionUtils.isEmptyElement(ids)) {
            return 0;
        }

        // 更新数据
        Device data = new Device();
        data.setAreaId(areaId);
        data.setMchId(mchId);
        DeviceQuery query = new DeviceQuery();
        query.setIds(ids);
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public DeviceVO selectAvaliableDevice(Long id, String sn) {
        if (id == null && StringUtils.isBlank(sn)) {
            return null;
        }
        DeviceQuery query = new DeviceQuery();
        if (id != null) {
            query.setId(id);
        } else if (StringUtils.hasText(sn)) {
            query.setEqSn(sn);
        }
        DeviceVO device = this.selectOne(query);
        ServiceUtil.assertion(!DeviceStatus.AVAILABLE.getCode().equals(device.getStatus()),
                "当前设备不可用：" + DeviceStatus.getNameByCode(device.getStatus()) );
        return device;
    }

    @Override
    public DeviceVO selectByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        }
        DeviceQuery query = new DeviceQuery();
        query.setEqMac(mac);
        return this.selectOne(query);
    }

    @Override
    public int unbindMch(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        for (DeviceVO device : deviceList) {
            ServiceUtil.assertion(device == null, "设备不能为空");
            ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
            ServiceUtil.assertion(DeviceUtil.isOrderUsing(device), "设备%s正在使用，不能解绑", device.getSn());
        }
        List<Long> ids = CollectionUtils.map(deviceList, DeviceVO::getId);
        return deviceMapper.unbindMch(ids);
    }

    @Override
    public int unbindArea(List<DeviceVO> deviceList) {
        if (CollectionUtils.isEmptyElement(deviceList)) {
            return 0;
        }
        for (DeviceVO device : deviceList) {
            ServiceUtil.assertion(device == null, "设备不能为空");
            ServiceUtil.assertion(device.getId() == null, "设备ID不能为空");
            ServiceUtil.assertion(DeviceUtil.isOrderUsing(device), "设备%s正在使用，不能解绑", device.getSn());
        }
        List<Long> ids = CollectionUtils.map(deviceList, DeviceVO::getId);
        return deviceMapper.unbindArea(ids);
    }

    @Override
    public int updateLastUserId(Long id, Long lastUserId) {
        if (id == null || lastUserId == null) {
            return 0;
        }
        Device data = new Device();
        data.setId(id);
        data.setLastUserId(lastUserId);
        return deviceMapper.updateDevice(data);
    }

    @Override
    public Long selectOrderIdByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        }
        return deviceMapper.selectOrderIdByMac(mac);
    }

    @Override
    public List<DeviceMacSnVO> listSnByMac(List<String> macList) {
        if (CollectionUtils.isEmptyElement(macList)) {
            return Collections.emptyList();
        }
        DeviceQuery query = new DeviceQuery();
        query.setMacList(macList);
        return deviceMapper.selectMacSnList(query);
    }

    @Override
    public int updateByQuery(Device data, DeviceQuery query) {
        return deviceMapper.updateByQuery(data, query);
    }

    @Override
    public DeviceVO selectDevice(Long id, String sn, boolean scope, AreaJoinPermission permission) {
        DeviceQuery query = new DeviceQuery();
        if (id != null) {
            query.setId(id);
        } else if (sn != null) {
            query.setSn(sn);
        } else {
            return null;
        }
        query.addAreaPermission(permission.getCode());
        return this.selectOne(query);
    }

    @Override
    public int updateByQuerySimple(Device data, DeviceQuery query) {
        return deviceMapper.updateByQuerySimple(data, query);
    }

    /**
     * 批量更新设备IOT数据
     *
     * @param deviceList 设备列表
     * @return 更新成功的记录数
     */
    @Override
    public int batchUpdateIot(List<Device> deviceList) {
        if (CollectionUtils.isEmpty(deviceList)) {
            return 0;
        }

        // 按相同字段分组，提高更新效率
        Map<String, List<Device>> groupMap = new HashMap<>();
        for (Device device : deviceList) {
            String key = getUpdateKey(device);
            groupMap.computeIfAbsent(key, k -> new ArrayList<>()).add(device);
        }

        int total = 0;
        // 分组批量更新
        for (List<Device> group : groupMap.values()) {
            // 分批处理，每批500条
            int batchSize = 500;
            for (int i = 0; i < group.size(); i += batchSize) {
                int endIndex = Math.min(i + batchSize, group.size());
                List<Device> batch = group.subList(i, endIndex);
                total += deviceMapper.batchUpdateIot(batch);
            }
        }
        return total;
    }

    /**
     * 获取设备更新字段的key
     * 相同字段的设备会被分到同一组进行更新
     */
    private String getUpdateKey(Device device) {
        StringBuilder key = new StringBuilder();
        key.append(device.getVoltage() != null ? "1" : "0")
                .append(device.getSignalStrength() != null ? "1" : "0")
                .append(device.getQuality() != null ? "1" : "0")
                .append(device.getSatellites() != null ? "1" : "0")
                .append(device.getIotStatus() != null ? "1" : "0")
                .append(device.getLongitude() != null ? "1" : "0")
                .append(device.getLatitude() != null ? "1" : "0")
                .append(device.getLastTime() != null ? "1" : "0")
                .append(device.getLastLocationTime() != null ? "1" : "0")
                .append(device.getOnlineStatus() != null ? "1" : "0")
                .append(device.getLastOnlineTime() != null ? "1" : "0")
                .append(device.getSoftwareVersion() != null ? "1" : "0");
        return key.toString();
    }

    @Override
    public List<String> selectAllMac() {
        List<String> macList = redisCache.getCacheObject(CacheConstants.ALL_MAC);
        if (CollectionUtils.isEmptyElement(macList)) {
            macList = deviceMapper.selectMacList(new DeviceQuery());
            if (CollectionUtils.isNotEmptyElement(macList)) {
                redisCache.setCacheObject(CacheConstants.ALL_MAC, macList);
            }
        }
        return macList;
    }

    public void clearCache() {
        redisCache.deleteObject(CacheConstants.ALL_MAC);
    }

}
