/*
*  Copyright 2019-2025 Tz
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.front.domain.dto.DeviceLogCriteria;
import me.zhengjie.modules.front.service.WeChatService;
import me.zhengjie.modules.security.config.enums.CurrentStatusEnum;
import me.zhengjie.modules.security.config.enums.ErrorStatusEnum;
import me.zhengjie.modules.security.config.enums.PrintInfoStatusEnum;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.domain.*;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.mapper.*;
import me.zhengjie.modules.system.service.BusDeviceService;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.zhengjie.utils.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;

/**
* @description 服务实现
* @author tz
* @date 2025-05-20
**/
@Service
@RequiredArgsConstructor
public class BusDeviceServiceImpl extends ServiceImpl<BusDeviceMapper, BusDevice> implements BusDeviceService {

    private final BusDeviceMapper busDeviceMapper;
    private final BusCommandMapper busCommandMapper;
    private final BusUserDeviceMapper userDeviceMapper;
    private final BusUserCommandMapper busUserCommandMapper;
    private final BusCommandLogMapper busCommandLogMapper;
    private final WeChatService weChatService;
    private final RedisUtils redisUtils;

    @Override
    public BusDevice getByDeviceCode(String deviceCode) {
        return busDeviceMapper.selectOne(new LambdaQueryWrapper<BusDevice>().in(BusDevice::getDeviceCode, deviceCode));
    }

    @Override
    public PageResult<BusCommandLog> deviceLogList(DeviceLogCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(busCommandLogMapper.deviceLogList(criteria.getDeviceId(), page));
    }

    @Override
    public BusDevice getByDeviceSn(String deviceSn) {
        return busDeviceMapper.selectOne(new LambdaQueryWrapper<BusDevice>().eq(BusDevice::getDeviceSn, deviceSn));
    }

    @Async
    @Override
    public void updateDeviceStatus(String deviceSn, CurrentStatusEnum e1, PrintInfoStatusEnum e2, ErrorStatusEnum e3, String remark) {
        busDeviceMapper.updateDeviceStatus(deviceSn, e1.getCode(), e2.getCode(), e3.getCode(), remark);
    }

    @Async
    @Override
    public void updateOrAdd(BusDevice entity) {
        BusDevice device = getByDeviceSn(entity.getDeviceSn());
        if (null == device) {
            entity.setDeviceCode((new Random().nextInt(900000) + 100000) + "");
            busDeviceMapper.insert(entity);
            device = entity;
        }
        List<BusDeviceCommand> newList = entity.getCommandList();
        List<BusDeviceCommand> oldList = busCommandMapper.selectList(new LambdaQueryWrapper<BusDeviceCommand>().eq(BusDeviceCommand::getDeviceId, device.getId()));
        if (newList.isEmpty() && !oldList.isEmpty()) {
            List<Long> dIds = oldList.stream().map(BusDeviceCommand::getId).collect(Collectors.toList());
            busCommandMapper.deleteBatchIds(dIds);
        } else if (!newList.isEmpty()) {
            // 查询第一个列表中 CommandType 在第二个列表中不存在的元素
            List<BusDeviceCommand> addList = newList.stream().filter(p1 -> oldList.stream().noneMatch(p2 -> Objects.equals(p1.getCommandType(), p2.getCommandType()))).collect(Collectors.toList());
            for (BusDeviceCommand add : addList) {
                add.setDeviceId(device.getId());
                busCommandMapper.insert(add);
            }
            // 查询第二个列表中 CommandType 在第一个列表中不存在的元素
            List<BusDeviceCommand> updList = oldList.stream().filter(p2 -> newList.stream().noneMatch(p1 -> Objects.equals(p2.getCommandType(), p1.getCommandType()))).collect(Collectors.toList());
            for (BusDeviceCommand upd : updList) {
                upd.setStatus(0); // 修改成指令无效
                busCommandMapper.updateById(upd);
            }
        }
    }

    @Override
    public PageResult<BusDevice> myDeviceList(Long userId, Page<Object> page) {
        return PageUtil.toPage(busDeviceMapper.myDeviceList(userId, page));
    }

    @Override
    public PageResult<BusDevice> queryAll(BusDeviceQueryCriteria criteria, Page<Object> page){
        return PageUtil.toPage(busDeviceMapper.findAll(criteria, page));
    }

    @Override
    public List<BusDevice> queryAll(BusDeviceQueryCriteria criteria){
        return busDeviceMapper.findAll(criteria);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(BusDevice resources) {
        String deviceSn = resources.getDeviceSn();
        BusDevice device = getByDeviceSn(deviceSn);
        if (null != device) {
            throw new BadRequestException("设备已存在，请勿重复添加");
        }
        resources.setDeviceCode((new Random().nextInt(900000) + 100000) + "");
        busDeviceMapper.insert(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(BusDevice resources) {
        BusDevice busDevice = getById(resources.getId());
        busDevice.copy(resources);
        busDeviceMapper.updateById(busDevice);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        busDeviceMapper.deleteBatchIds(ids);
    }

    @Override
    public String getQrCode(Long id) {
        // String code = (new Random().nextInt(900000) + 100000) + "";
        BusDevice device = busDeviceMapper.selectById(id);
        String qrCode = weChatService.getQrCode(id, device.getDeviceCode());
        // redisUtils.set(CacheKey.QR_CODE + code, id, 1, TimeUnit.DAYS);
        return qrCode;
    }

    @Override
    public void bindingDevice(String deviceCode) {
        BusDevice device = busDeviceMapper.selectOne(new LambdaQueryWrapper<BusDevice>().eq(BusDevice::getDeviceCode, deviceCode));
        if (null == device) {
            throw new BadRequestException("二维码有误，未查询到设备信息");
        }
        JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser();
        User user = jwtUser.getUser();
        BusUserDevice udEntity = userDeviceMapper.selectOne(new LambdaQueryWrapper<BusUserDevice>()
                .eq(BusUserDevice::getDeviceId, device.getId())
                .eq(BusUserDevice::getUserId, user.getId())
        );
        if (null != udEntity) {
            throw new BadRequestException("您已绑定过该设备，请勿重复绑定");
        }
        BusUserDevice addUserDevice = new BusUserDevice();
        addUserDevice.setUserId(user.getId());
        addUserDevice.setDeviceId(device.getId());
        userDeviceMapper.insert(addUserDevice);
        List<BusDeviceCommand> commands = busCommandMapper.selectList(new LambdaQueryWrapper<BusDeviceCommand>().eq(BusDeviceCommand::getDeviceId, device.getId()));
        if (commands.isEmpty()) { return; }
        List<Long> commandIds = commands.stream().map(BusDeviceCommand::getId).collect(Collectors.toList());
        busUserCommandMapper.bindingCommand(user.getId(), commandIds);
    }

    @Override
    public List<BusTreeVo> getDeviceTree(Long pid) {
        List<BusTreeVo> result;
        if(pid != null && !pid.equals(0L)){
            result = busCommandMapper.getCommandByDeviceId(pid);
        } else {
            result = busDeviceMapper.getDevice();
        }
        return result;
    }
}