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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.DeviceVo;
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.repo.ProductRepository;
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;


    /**
     * 新建设备
     */
    @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) {
        if (dto.getNum() == null || dto.getNum() <= 0) {
            throw new ConstraintException("设备数量必须大于0！");
        }
        if (dto.getPrefix() == null) {
            throw new ConstraintException("前缀不能为空！");
        }
        if (dto.getProductId() == null) {
            throw new ConstraintException("产品ID不能为空！");
        }
        // 获取产品信息
        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) {
        IPage<Device> devicePage = this.page(page, query.wrapper());
        List<Long> productIds = devicePage.getRecords().stream()
                .map(Device::getProductId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        LambdaQueryWrapper<Product> productQueryWrapper = new LambdaQueryWrapper<>();
        productQueryWrapper.in(Product::getId, productIds);
        List<Product> products = productRepo.selectList(productQueryWrapper);
        Map<Long, String> productMap = products.stream()
                .collect(Collectors.toMap(Product::getId, Product::getName));
        devicePage.getRecords().forEach(device -> {
            if (device.getProductId() != null) {
                device.setProductName(productMap.get(device.getProductId()));
            }
        });
        return devicePage;
    }


}
