package cn.iocoder.yudao.module.iot.dal.mysql.device;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.NewIotDeviceDO;
import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;

import javax.annotation.Nullable;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * IoT 设备 Mapper
 *
 * @author 芋道源码
 */
@Mapper
public interface NewIotDeviceMapper extends BaseMapperX<NewIotDeviceDO> {

    default PageResult<NewIotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<NewIotDeviceDO>()
                .likeIfPresent(NewIotDeviceDO::getDeviceName, reqVO.getDeviceName())
                .eqIfPresent(NewIotDeviceDO::getProductId, reqVO.getProductId())
                .eqIfPresent(NewIotDeviceDO::getDeviceType, reqVO.getDeviceType())
                .likeIfPresent(NewIotDeviceDO::getNickname, reqVO.getNickname())
                .eqIfPresent(NewIotDeviceDO::getState, reqVO.getStatus())
                .eqIfPresent(NewIotDeviceDO::getGatewayId, reqVO.getGatewayId())
                .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0")
                .orderByDesc(NewIotDeviceDO::getId));
    }

    default NewIotDeviceDO selectByDeviceName(String deviceName) {
        return selectOne(NewIotDeviceDO::getDeviceName, deviceName);
    }

    default NewIotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) {
        return selectOne(NewIotDeviceDO::getProductKey, productKey,
                NewIotDeviceDO::getDeviceName, deviceName);
    }

    default long selectCountByGatewayId(Long id) {
        return selectCount(NewIotDeviceDO::getGatewayId, id);
    }

    default Long selectCountByProductId(Long productId) {
        return selectCount(NewIotDeviceDO::getProductId, productId);
    }

    default List<NewIotDeviceDO> selectListByCondition(@Nullable Integer deviceType,
                                                       @Nullable Long productId) {
        return selectList(new LambdaQueryWrapperX<NewIotDeviceDO>()
                .eqIfPresent(NewIotDeviceDO::getDeviceType, deviceType)
                .eqIfPresent(NewIotDeviceDO::getProductId, productId));
    }

    default List<NewIotDeviceDO> selectListByState(Integer state) {
        return selectList(NewIotDeviceDO::getState, state);
    }

    default List<NewIotDeviceDO> selectListByProductId(Long productId) {
        return selectList(NewIotDeviceDO::getProductId, productId);
    }

    default Long selectCountByGroupId(Long groupId) {
        return selectCount(new LambdaQueryWrapperX<NewIotDeviceDO>()
                .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0"));
    }

    default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) {
        return selectCount(new LambdaQueryWrapperX<NewIotDeviceDO>()
                .geIfPresent(NewIotDeviceDO::getCreateTime, createTime));
    }

    default List<NewIotDeviceDO> selectByProductKeyAndDeviceNames(String productKey, Collection<String> deviceNames) {
        return selectList(new LambdaQueryWrapperX<NewIotDeviceDO>()
                .eq(NewIotDeviceDO::getProductKey, productKey)
                .in(NewIotDeviceDO::getDeviceName, deviceNames));
    }

    default NewIotDeviceDO selectBySerialNumber(String serialNumber) {
        return selectOne(NewIotDeviceDO::getSerialNumber, serialNumber);
    }

    /**
     * 查询指定产品下的设备数量
     *
     * @return 产品编号 -> 设备数量的映射
     */
    default Map<Long, Integer> selectDeviceCountMapByProductId() {
        List<Map<String, Object>> result = selectMaps(new QueryWrapper<NewIotDeviceDO>()
                .select("product_id AS productId", "COUNT(1) AS deviceCount")
                .groupBy("product_id"));
        return result.stream().collect(Collectors.toMap(
            map -> Long.valueOf(map.get("productId").toString()),
            map -> Integer.valueOf(map.get("deviceCount").toString())
        ));
    }

    /**
     * 查询各个状态下的设备数量
     *
     * @return 设备状态 -> 设备数量的映射
     */
    default Map<Integer, Long> selectDeviceCountGroupByState() {
        List<Map<String, Object>> result = selectMaps(new QueryWrapper<NewIotDeviceDO>()
                .select("state", "COUNT(1) AS deviceCount")
                .groupBy("state"));
        return result.stream().collect(Collectors.toMap(
            map -> Integer.valueOf(map.get("state").toString()),
            map -> Long.valueOf(map.get("deviceCount").toString())
        ));
    }

    /**
     * 查询有位置信息的设备列表
     *
     * @return 设备列表
     */
    default List<NewIotDeviceDO> selectListByHasLocation() {
        return selectList(new LambdaQueryWrapperX<NewIotDeviceDO>()
                .isNotNull(NewIotDeviceDO::getLatitude)
                .isNotNull(NewIotDeviceDO::getLongitude));
    }

    // ========== 网关-子设备绑定相关 ==========

    /**
     * 根据网关编号查询子设备列表
     *
     * @param gatewayId 网关设备编号
     * @return 子设备列表
     */
    default List<NewIotDeviceDO> selectListByGatewayId(Long gatewayId) {
        return selectList(NewIotDeviceDO::getGatewayId, gatewayId);
    }

    /**
     * 分页查询未绑定网关的子设备
     *
     * @param reqVO 分页查询参数
     * @return 子设备分页
     */
    default PageResult<NewIotDeviceDO> selectUnboundSubDevicePage(IotDevicePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<NewIotDeviceDO>()
                .likeIfPresent(NewIotDeviceDO::getDeviceName, reqVO.getDeviceName())
                .likeIfPresent(NewIotDeviceDO::getNickname, reqVO.getNickname())
                .eqIfPresent(NewIotDeviceDO::getProductId, reqVO.getProductId())
                // 仅查询子设备 + 未绑定网关
                .eq(NewIotDeviceDO::getDeviceType, IotProductDeviceTypeEnum.GATEWAY_SUB.getType())
                .isNull(NewIotDeviceDO::getGatewayId)
                .orderByDesc(NewIotDeviceDO::getId));
    }

    /**
     * 批量更新设备的网关编号
     *
     * @param ids       设备编号列表
     * @param gatewayId 网关设备编号（可以为 null，表示解绑）
     */
    default void updateGatewayIdBatch(Collection<Long> ids, Long gatewayId) {
        update(null, new LambdaUpdateWrapper<NewIotDeviceDO>()
                .set(NewIotDeviceDO::getGatewayId, gatewayId)
                .in(NewIotDeviceDO::getId, ids));
    }

}
