package com.zsc.edu.gateway.modules.iot.device.service.impl;

import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.exception.ConstraintException;
import com.zsc.edu.gateway.exception.OutlineException;
import com.zsc.edu.gateway.framework.redis.RedisUtils;
import com.zsc.edu.gateway.modules.iot.device.dto.BatchDeviceDto;
import com.zsc.edu.gateway.modules.iot.device.dto.DeviceDto;
import com.zsc.edu.gateway.modules.iot.device.dto.DeviceServeDto;
import com.zsc.edu.gateway.modules.iot.device.entity.Device;
import com.zsc.edu.gateway.modules.iot.device.mapper.DeviceMapper;
import com.zsc.edu.gateway.modules.iot.device.query.DeviceQuery;
import com.zsc.edu.gateway.modules.iot.device.repo.DeviceRepository;
import com.zsc.edu.gateway.modules.iot.device.service.DeviceService;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceStatusVo;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceVo;
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.repo.ProductRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.EventRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.PropertyRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.ServeRepository;
import com.zsc.edu.gateway.modules.mqtt.config.MqttConfig;
import com.zsc.edu.gateway.modules.mqtt.config.MqttSender;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 15864
 */
@Slf4j
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceRepository, Device> implements DeviceService {

    @Resource
    private DeviceMapper mapper;
    @Resource
    private ProductRepository productRepo;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PropertyRepository propertyRepo;
    @Resource
    private EventRepository eventRepo;
    @Resource
    private ServeRepository serveRepo;
    @Resource
    private MqttSender mqttSender;
    @Resource
    private DeviceRepository deviceRepo;
    @Resource
    private MqttConfig mqttConfig;
    /**
     * 新建设备
     */
    @Override
    @Transactional
    public Device create(DeviceDto dto) {
        if (baseMapper.findByName(dto.getName()) != null) {
            throw new ConstraintException("该设备名称已存在！");
        }
        Device device = mapper.toEntity(dto);
        save(device);
        return device;
    }


    /**
     * 批量新增
     */
    @Override
    @Transactional
    public List<Device> batchCreate(BatchDeviceDto dto) {
        // 获取产品信息
        Product product = productRepo.selectById(dto.getProductId());
        if (product == null) {
            throw new ConstraintException("该产品不存在！");
        }
        // 生成设备名称列表
        List<String> names = new ArrayList<>();
        for (int i = 0; i < dto.getNum(); i++) {
            String name = String.format("%s%05d", dto.getPrefix(), i + 1);
            names.add(name);
        }
        // 检查设备名称是否已存在
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Device::getName, names);
        List<Device> existingDevices = baseMapper.selectList(queryWrapper);
        if (!existingDevices.isEmpty()) {
            List<String> existingNames = existingDevices.stream()
                    .map(Device::getName)
                    .collect(Collectors.toList());
            throw new ConstraintException("设备已存在！已存在的设备名称: " + String.join(", ", existingNames));
        }
        // 创建设备列表
        List<Device> devices = new ArrayList<>();
        for (int i = 0; i < dto.getNum(); i++) {
            String name = String.format("%s%05d", dto.getPrefix(), i + 1);
            DeviceDto deviceDto = new DeviceDto();
            deviceDto.setName(name);
            deviceDto.setProductId(product.getId());
            deviceDto.setExtendParams(dto.getExtendParams());
            deviceDto.setProperties(dto.getProperties());
            deviceDto.setFirmwareVersion(dto.getFirmwareVersion());
            deviceDto.setHardwareVersion(dto.getHardwareVersion());
            Device device = mapper.toEntity(deviceDto);
            String productType = product.getProductType();
            int typeCode = Math.abs(productType.hashCode()) % 100;
            device.setClientId(String.format("%s%02d%02d%05d", "001", typeCode, 3, i + 1));
            devices.add(device);
        }
        // 批量插入设备
        baseMapper.insert(devices);
        return devices;
    }


    /**
     * 更新设备
     */
    @Override
    @Transactional
    public Device update(DeviceDto dto, Long id) {
        Device device = baseMapper.selectById(id);
        if (device == null) {
            throw new ConstraintException("设备不存在！");
        }
        mapper.convert(dto, device);
        updateById(device);
        return device;
    }

    /**
     * 通过Id查找设备详情
     *
     * @param id
     * @return 设备详情
     */
    @Override
    public DeviceVo detail(Long id) {
        return baseMapper.findById(id);
    }

    /**
     * 物模型配置的服务
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean serve(DeviceServeDto dto) {
        Device device = baseMapper.findByClientIdAndStateAndOnline(dto.clientId, Device.Status.ACTIVATED, true);
        if (device != null) {
            // amqpTemplate.convertAndSend(exchange, "send", json);
            redisUtils.set("serve:sendTime:" + dto.serveName + ":" + dto.clientId, String.valueOf(new Date()));
            return true;
        } else {
            throw new OutlineException("设备[" + dto.clientId + "]不在线！");
        }
    }

    /**
     * 查询设备列表
     */
    @Override
    public IPage<Device> query(Page<Device> page, DeviceQuery query) {
        return deviceRepo.selectPage(page, query.wrapper());
    }

    @Override
    public DeviceStatusVo status() {
        return DeviceStatusVo.builder()
                .deviceCount(baseMapper.selectCount(new QueryWrapper<>()))
                .propertyCount(propertyRepo.selectCount(new QueryWrapper<>()))
                .serveCount(serveRepo.selectCount(new QueryWrapper<>()))
                .eventCount(eventRepo.selectCount(new QueryWrapper<>()))
                .onlineCount(baseMapper.selectCount(new LambdaQueryWrapper<Device>().eq(Device::getOnline, true)))
                .offlineCount(baseMapper.selectCount(new LambdaQueryWrapper<Device>().eq(Device::getOnline, false)))
                .disabledCount(baseMapper.selectCount(new LambdaQueryWrapper<Device>().eq(Device::getState, Device.Status.UNACTIVATED)))
                .directCount(productRepo.selectCount(new LambdaQueryWrapper<Product>().eq(Product::getLink, Product.LinkType.HTTP)))
                .gatewayCount(productRepo.selectCount(new LambdaQueryWrapper<Product>().eq(Product::getLink, Product.LinkType.TCP)))
                .gatewaySubCount(productRepo.selectCount(new LambdaQueryWrapper<Product>().eq(Product::getLink, Product.LinkType.MQTT)))
                .build();
    }

    @Override
    public String send(Long deviceId, Integer qos, JSONObject paras) throws JSONException {
        DeviceVo device = detail(deviceId);
        JSONObject payloadJson = new JSONObject();
        payloadJson.put("mid", 641);
        payloadJson.put("serviceId", "runParamsCommand");
        payloadJson.put("deviceId", device.getClientId());
        payloadJson.put("cmd", "runParam");
        payloadJson.put("paras", paras);
        payloadJson.put("msgType", "cloudReq");
        String payload = payloadJson.toString();
        String topic = "/r1/devices/" + device.getClientId() + "/datas";
        if (paras.isEmpty()) {
            throw new JSONException("参数不能为空");
        }
        try {
            mqttSender.sendMsg(topic, qos, payload);
        } catch (Exception e) {
            throw new JSONException("发送失败");
        }
        return payload;
    }

}
