package com.xy.service;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.xy.config.DeviceThreadPoolConfig;
import com.xy.dbmapper.SyncUpdate;
import com.xy.device.EnumDeviceTempConfig;
import com.xy.device.EnumMqttCmdTempletTask;
import com.xy.dto.CommandMqtt;
import com.xy.dto.DeviceEventMsgDto;
import com.xy.dto.DeviceStatusDto;
import com.xy.dto.DeviceTempRecordsDto;
import com.xy.entity.*;
import com.xy.mapper.DeviceStatusMapper;
import com.xy.mapper.entity.DeviceStatusCount;
import com.xy.utils.*;
import com.xy.utils.enums.DeviceErrorRecordTypesEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static com.xy.utils.Beans.copy;

/**
 * <p>
 * 设备-状态 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2022-12-23
 */
@Slf4j
@Service
@AllArgsConstructor(onConstructor_ = @Lazy)
@Api(tags = "设备-状态")
public class DeviceStatusServiceImpl extends ServiceImpl<DeviceStatusMapper, DeviceStatus> implements DeviceStatusService {

    private DeviceTempRecordsServiceImpl deviceTempRecordsService;

    private DeviceConfigServiceImpl deviceConfigService;

    private DeviceEventMsgService deviceEventMsgService;

    private RedisService<Integer> redisService;

    private DeviceSysinfoServiceImpl deviceSysinfoService;

    private RedisService<DeviceVersionUpServiceImpl.UpInfo> upInfoRedisService;

    private MqttServiceImpl mqttService;

    private DeviceDoorRecordServiceImpl deviceDoorRecordService;

    @Override
    @ApiOperation("上报信息")
    public R up(DeviceStatusDto.Up up) {
        log.info("设备信息上报：{}", JSONUtil.toJsonStr(up));
        ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.DEVICE_STATUS_UP, 3)
                .execute(() -> {
                    //修改状态信息
                    updateById(copy(DeviceStatus.class, up).setUpdateTime(LocalDateTime.now()));
                })
                .execute(() -> {
                    //温度上报
                    if (!Emptys.check(up.getTempValue())) {
                        return;
                    }
                    //添加设备温度日志
                    DeviceSysinfo deviceSysinfo = deviceSysinfoService.getById(up.getDeviceId());
                    if (!deviceSysinfo.getIsHaveTemp()) {
                        return;
                    }
                    DeviceTempRecordsDto.Save save = new DeviceTempRecordsDto.Save()
                            .setDeviceId(up.getDeviceId());
                    save.setTempValue(up.getTempValue());
                    deviceTempRecordsService.save(save);
                    //查询设备配置
                    DeviceConfig deviceConfig = deviceConfigService.getById(up.getDeviceId());
                    int tempMax, tempMin;
                    if (deviceConfig == null) {
                        Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get(EnumDeviceTempConfig.Code.CODE.getCode());
                        tempMax = Integer.valueOf(stringSysDictRedisMap.get(EnumDeviceTempConfig.MAX.getCode()).getValue());
                        tempMin = Integer.valueOf(stringSysDictRedisMap.get(EnumDeviceTempConfig.MIN.getCode()).getValue());
                    } else {
                        tempMax = deviceConfig.getTempMax();
                        tempMin = deviceConfig.getTempMin();
                    }
                    //温度有异常
                    String key = String.format("device_temp_error:%d", up.getDeviceId());
                    if (up.getTempValue() > tempMax || up.getTempValue() < tempMin) {
                        //校验异常次数
                        Integer errorSize = SysDictUtils.getValue(EnumDeviceTempConfig.Code.CODE.getCode(), EnumDeviceTempConfig.ERROR_SIZE.getCode(), Integer.class);
                        Integer maxSize = redisService.get(key);
                        maxSize = maxSize == null ? 1 : maxSize;
                        if (maxSize >= errorSize) {
                            //添加事件
                            String msg = "温度异常-温度阈值%d - %d,当前温度%d";
                            DeviceEventMsgDto.Save deviceEventMsg = new DeviceEventMsgDto.Save()
                                    .setDeviceId(up.getDeviceId());
                            deviceEventMsg.setCode(DeviceErrorRecordTypesEnum.T.getCode());
                            deviceEventMsg.setMsg(String.format(msg, tempMin, tempMax, up.getTempValue()));
                            deviceEventMsgService.save(deviceEventMsg);
                            redisService.remove(key);
                            return;
                        }
                        redisService.set(key, maxSize + 1, 60 * 60);
                        return;
                    }
                    redisService.remove(key);
                })
                .execute(() -> {
                    //检查是否可升级
                    String appKey = up.getAppKey();
                    String version = up.getVersion();
                    if (!Emptys.check(appKey) || !Emptys.check(version)) {
                        return;
                    }
                    String key = String.format(DeviceVersionUpServiceImpl.upInfoKey, appKey);
                    DeviceVersionUpServiceImpl.UpInfo upInfo = upInfoRedisService.get(key);
                    if (upInfo == null || !upInfo.getEnable()) {
                        return;
                    }
                    //下发升级指令
                    int a1 = Integer.parseInt(version.substring(0, version.lastIndexOf(".")).replaceAll("\\.", ""));
                    int a2 = Integer.parseInt(version.substring(version.lastIndexOf(".") + 1));
                    int b1 = Integer.parseInt(upInfo.getVersion().substring(0, upInfo.getVersion().lastIndexOf(".")).replaceAll("\\.", ""));
                    int b2 = Integer.parseInt(upInfo.getVersion().substring(upInfo.getVersion().lastIndexOf(".") + 1));
                    boolean isUp = (a1 < b1) || (a1 == b1 && a2 < b2);
                    if (!isUp) {
                        return;
                    }
                    String templetStr = SysDictUtils.getValue(EnumMqttCmdTempletTask.Code.CODE.getCode(), EnumMqttCmdTempletTask.APPUPDATE.getCode(), String.class);
                    JSONObject templet = JSONUtil.parseObj(templetStr);
                    JSONObject data = templet.getJSONObject("data")
                            .set(LambdaUtils.getProperty(DeviceVersionUpServiceImpl.UpInfo::getAppId), upInfo.getAppId())
                            .set(LambdaUtils.getProperty(DeviceVersionUpServiceImpl.UpInfo::getAppKey), upInfo.getAppKey())
                            .set(LambdaUtils.getProperty(DeviceVersionUpServiceImpl.UpInfo::getVersion), upInfo.getVersion())
                            .set(LambdaUtils.getProperty(DeviceVersionUpServiceImpl.UpInfo::getSrc), upInfo.getSrc());
                    templet.set("data", data);
                    CommandMqtt commandMqtt = new CommandMqtt()
                            .setDeviceId(up.getDeviceId())
                            .setTemplet(templet);
                    mqttService.senCommand(Arrays.asList(commandMqtt));
                });
        return R.ok();
    }

    @Override
    @ApiOperation("开关门")
    public R door(DeviceStatusDto.Door door) {
        LocalDateTime now = LocalDateTime.now();
        //修改门锁状态
        DeviceStatus deviceStatus = copy(DeviceStatus.class, door).setUpdateTime(LocalDateTime.now());
        updateById(deviceStatus);
        //增加记录
        FunctionUtils.ParamsNoResult<Tuple.Tuple2<Integer, Boolean>> fun = params -> deviceDoorRecordService.save(new DeviceDoorRecord()
                .setId(YitIdHelper.nextId())
                .setDeviceId(door.getDeviceId())
                .setDoor(params.getV1())
                .setStatus(params.getV2())
                .setType(door.getType())
                .setCreateTime(now)
        );
        if (Emptys.check(door.getDoorStateL())) {
            fun.run(new Tuple.Tuple2<>(0, door.getDoorStateL() == 1));
        }
        if (Emptys.check(door.getDoorStateR())) {
            fun.run(new Tuple.Tuple2<>(1, door.getDoorStateR() == 1));
        }
        return R.ok();
    }

    /**
     * 修改库存
     *
     * @return
     */
    public R upStock(long deviceId, int stock) {
        DeviceStatus deviceStatusInfo = getById(deviceId);
        if (deviceStatusInfo == null) {
            return R.ok();
        }
        new SyncUpdate().update(DeviceStatus.class)
                .set(DeviceStatus::getStock, new BigDecimal(stock))
                .udateDateTime(DeviceStatus::getUpdateTime)
                .eq(DeviceStatus::getDeviceId, deviceId)
                .exec();
        return R.ok();
    }

    @Override
    @ApiOperation("更新联网状态")
    public R updateNetState(DeviceStatusDto.UpdateNetState dto) {
        updateById(new DeviceStatus().setDeviceId(dto.getDeviceId()).setNetState(dto.getNetState()));
        return R.ok();
    }


    @PostMapping("obj")
    @ApiOperation("对象查询")
    public R<DeviceStatusDto.Vo> obj(@RequestBody DeviceStatusDto.Vo vo) {
        LambdaQueryWrapper<DeviceStatus> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(vo, DeviceStatus.class).build();
        List<DeviceStatus> list = list(lambdaQueryWrapper);
        if (!Emptys.check(list)) {
            return R.ok();
        }
        return R.ok(copy(DeviceStatusDto.Vo.class, list.get(0)));
    }

    @PostMapping("list")
    @ApiOperation("集合查询")
    public R<List<DeviceStatusDto.Vo>> list(@RequestBody DeviceStatusDto.SelectList selectList) {
        LambdaQueryWrapper<DeviceStatus> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(selectList, DeviceStatus.class)
                .in(DeviceStatus::getDeviceId, selectList.getDeviceIds())
                .build();
        List<DeviceStatus> list = list(lambdaQueryWrapper);
        return R.ok(copy(DeviceStatusDto.Vo.class, list));
    }

    @PostMapping("statusCount")
    @ApiOperation("统计状态数量")
    public R<DeviceStatusCount> statusCount() {
        DeviceStatusCount deviceStatusCount = baseMapper.statusCount();
        return R.ok(deviceStatusCount);
    }
}
