package com.xy.service.factory.device.impl.open;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xy.annotate.Factory;
import com.xy.collections.list.JArrayList;
import com.xy.collections.map.JMap;
import com.xy.device.EnumDeviceActiveStatus;
import com.xy.device.EnumDeviceBusyStatus;
import com.xy.dto.*;
import com.xy.dto.be.MercDto;
import com.xy.dto.nfc.ActiveDeviceDTO;
import com.xy.dto.nfc.ResetDeviceDTO;
import com.xy.entity.*;
import com.xy.enums.SysCodeConfigureEnum;
import com.xy.event.DeviceEvent;
import com.xy.mapper.DeviceCreateIdsMapper;
import com.xy.mapper.DeviceInfoMapper;
import com.xy.mapper.DeviceStatusMapper;
import com.xy.mapper.DeviceSysinfoMapper;
import com.xy.service.*;
import com.xy.service.be.MercService;
import com.xy.service.factory.cmd.CmdService;
import com.xy.service.factory.device.DeviceFactory;
import com.xy.utils.*;
import com.xy.utils.Enum.AlgorithmTypeEnum;
import com.xy.utils.enums.DeviceTypeEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

/**
 * 开门柜设备 接口实现
 */
@Slf4j
@Factory(type = "1")
@AllArgsConstructor
public class OpenDeviceFactoryImpl implements DeviceFactory {

    private DeviceCreateIdsMapper deviceCreateIdsMapper;

    private DeviceInfoMapper deviceInfoMapper;

    private DeviceSysinfoMapper deviceSysinfoMapper;

    private DeviceStatusMapper deviceStatusMapper;

    private MqttUserServiceImpl mqttUserService;

    private DeviceRegisterServiceImpl deviceRegisterService;

    private DeviceInfoServiceImpl deviceInfoService;

    private MqttServiceImpl mqttService;

    private DeviceEventMsgServiceImpl deviceEventMsgService;

    private DeviceMqttSendService deviceMqttSendService;

    private DeviceChargingServiceImpl deviceChargingService;

    private DeviceAlgorithmChargingServiceImpl deviceAlgorithmChargingService;

    private DeviceFaultMonitorConfigServiceImpl deviceFaultMonitorConfigService;

    private MercService mercService;

    private AlipayDeviceService alipayDeviceService;

    private MercDeviceCostConfigService mercDeviceCostConfigService;

    @Override
    public R save(DeviceRegisterDto.Save save) {
        LocalDateTime now = LocalDateTime.now();
        DeviceRegister deviceRegister = copy(DeviceRegister.class, save.setDeviceSn(save.getDeviceSn().toUpperCase()));
        deviceRegister.setDeviceType(save.getDeviceType())
                .setCreateUser(AuthorizeUtils.getLoginId(Long.class))
                .setCreateTime(now);
        //操作注册序号
        String property = LambdaUtils.getProperty(DeviceRegister::getRegisterNum);
        QueryWrapper<DeviceRegister> queryWrapper = new QueryWrapper<DeviceRegister>()
                .select(String.format("max(%s) as %s", StringTools.humpToLine(property), property))
                .eq(StringTools.humpToLine(LambdaUtils.getProperty(DeviceRegister::getCreateUser)), AuthorizeUtils.getLoginId(Long.class));
        DeviceRegister one = deviceRegisterService.getOne(queryWrapper);
        Integer registerNum = one == null || one.getRegisterNum() == null ? 0 : one.getRegisterNum();
        deviceRegister.setRegisterNum(registerNum + 1);
        //修改设备-机器ID号生成为已绑定
        deviceCreateIdsMapper.updateById(new DeviceCreateIds()
                .setDeviceId(deviceRegister.getDeviceId())
                .setBindState(true)
                .setBindTime(deviceRegister.getCreateTime())
        );
        DeviceInfo deviceInfo = new DeviceInfo()
                .setDeviceId(deviceRegister.getDeviceId())
                .setDeviceType(deviceRegister.getDeviceType())
                .setMercDeviceCode(deviceRegister.getAssetNo())
                .setCreateTime(deviceRegister.getCreateTime());
        if (deviceRegister.getDeviceType() == DeviceTypeEnum.TYPE5.getCode().intValue()) {
            deviceInfo.setAlgorithmId(AlgorithmTypeEnum.ALIPAY.getId());
        }
        DeviceSysinfo deviceSysinfo = new DeviceSysinfo()
                .setDeviceId(deviceRegister.getDeviceId())
                .setDeviceSn(deviceRegister.getDeviceSn())
                .setCreateTime(deviceRegister.getCreateTime());
        DeviceStatus deviceStatus = new DeviceStatus().setDeviceId(deviceRegister.getDeviceId());
        DeviceRegister deviceRegisterById = deviceRegisterService.getById(deviceRegister.getDeviceId());
        //新增
        if (deviceRegisterById == null) {
            //设备-注册登记
            deviceRegisterService.save(deviceRegister);
            //设备信息
            deviceInfoMapper.insert(deviceInfo);
            //设备-系统信息
            deviceSysinfoMapper.insert(deviceSysinfo);
            //设备-状态
            deviceStatusMapper.insert(deviceStatus);
            //mqtt认证信息
            String deviceId = deviceRegister.getDeviceId().toString();
            MqttUserDto.Save mqttUserInfo = new MqttUserDto.Save();
            mqttUserInfo.setUsername(deviceId);
            mqttUserInfo.setPasswordHash(deviceId);
            mqttUserInfo.setCreated(LocalDateTime.now());
            mqttUserInfo.setTopic(deviceId + CommConsts.DEVICE_MQTT_TOPIC_SUFFIX);
            mqttUserInfo.setSn(deviceRegister.getDeviceSn());
            mqttUserInfo.setDeviceId(deviceRegister.getDeviceId());
            mqttUserService.save(mqttUserInfo);
        } else {
            deviceRegisterService.updateById(deviceRegister);
            deviceSysinfoMapper.updateById(deviceSysinfo);
            mqttUserService.update(new LambdaUpdateWrapper<MqttUser>()
                    .set(MqttUser::getSn, deviceRegister.getDeviceSn())
                    .eq(MqttUser::getDeviceId, deviceRegister.getDeviceId())
            );
        }
        return R.ok();
    }

    @Override
    public R<List<MercMiniDeviceDto.ActiveVo>> active(MercMiniDeviceDto.Active2 active) {
        List<MercMiniDeviceDto.ActiveVo> result = new ArrayList<>();
        List<Long> deviceIds = active.getDeviceId();
        //机器是否已激活
        Integer activeState = SysDictUtils.getValue(EnumDeviceActiveStatus.Code.CODE.getCode(), EnumDeviceActiveStatus.N_1.getCode(), Integer.class);
        long count = deviceInfoService.count(new LambdaQueryWrapper<DeviceInfo>()
                .in(DeviceInfo::getDeviceId, deviceIds)
                .eq(DeviceInfo::getActiveState, activeState)
        );
        if (count > 0) {
            return R.fail("机器已激活");
        }
        DeviceInfo deviceInfo = deviceInfoService.getById(deviceIds.get(0));
        //分账服务费模式无法自主激活
        MercDeviceCostConfigDto.Vo mercDeviceCostConfig = mercDeviceCostConfigService.query(new MercDeviceCostConfigDto.Query()
                .setMercId(deviceInfo.getMercId())
                .setType(1)
        ).getData();
        if (mercDeviceCostConfig != null) {
            return R.fail("分账服务费模式商家无需付款激活，请联系运营人员免费激活");
        }
        //提现模式商户无需算法费
        Boolean isDraw = mercService.isDraw(new MercDto.IsDraw().setMercId(deviceInfo.getMercId())).getData();
        if (isDraw) {
            active.setIsDeviceAlgorithmCharging(false);
        }
        //检查费用
        deviceIds.forEach(deviceId -> {
            R deviceChargingCheck = active.getIsDeviceCharging() ? deviceChargingService.check(new DeviceChargingDto.Check().setDeviceId(deviceId)) : R.ok();
            R deviceAlgorithmChargingCheck = active.getIsDeviceAlgorithmCharging() ? deviceAlgorithmChargingService.check(new DeviceAlgorithmChargingDto.Check().setDeviceId(deviceId)) : R.ok();
            if (deviceChargingCheck.getCode() != R.Enum.SUCCESS.getCode() && deviceAlgorithmChargingCheck.getCode() != R.Enum.SUCCESS.getCode()) {
                MercMiniDeviceDto.ActiveVo activeVo = new MercMiniDeviceDto.ActiveVo()
                        .setDeviceId(deviceId)
                        .setCode(R.Enum.CHARGING_ALGORITHM_CHARGING.getCode())
                        .setMsg(R.Enum.CHARGING_ALGORITHM_CHARGING.getMsg());
                result.add(activeVo);
            } else {
                if (deviceChargingCheck.getCode() != R.Enum.SUCCESS.getCode()) {
                    MercMiniDeviceDto.ActiveVo activeVo = new MercMiniDeviceDto.ActiveVo()
                            .setDeviceId(deviceId)
                            .setCode(deviceChargingCheck.getCode())
                            .setMsg(deviceChargingCheck.getMsg());
                    result.add(activeVo);
                }
                if (deviceAlgorithmChargingCheck.getCode() != R.Enum.SUCCESS.getCode()) {
                    MercMiniDeviceDto.ActiveVo activeVo = new MercMiniDeviceDto.ActiveVo()
                            .setDeviceId(deviceId)
                            .setCode(deviceAlgorithmChargingCheck.getCode())
                            .setMsg(deviceAlgorithmChargingCheck.getMsg());
                    result.add(activeVo);
                }
            }
        });
        //激活
        LocalDateTime now = LocalDateTime.now();
        Integer busyState = SysDictUtils.getValue(EnumDeviceBusyStatus.Code.CODE.getCode(), EnumDeviceBusyStatus.N_1.getCode(), Integer.class);
        List<DeviceInfo> activeList = new ArrayList<>();
        JMap<Long, MercMiniDeviceDto.ActiveVo> cover = new JArrayList<>(result).toMap(MercMiniDeviceDto.ActiveVo::getDeviceId).cover();
        deviceIds.forEach(deviceId -> {
            DeviceInfoDto.Vo dv = R.feignCheckData(deviceInfoService.obj(new DeviceInfoDto.Obj().setDeviceId(deviceId).setIsSysinfo(Boolean.TRUE)));
            DeviceSysinfoDto.Vo deviceSysinfo = dv.getDeviceSysinfo();
            if (deviceSysinfo != null && deviceSysinfo.getIsHaveAlipayNpad()) {
                String nfcSn = deviceSysinfo.getNfcSn();
                if (StrUtil.isEmpty(nfcSn)) {
                    throw new RuntimeException("nfc sn 不存在!");
                }
                String deviceName = dv.getDeviceName();
                if (StrUtil.isEmpty(deviceName)) {
                    deviceName = "" + deviceId;
                }
                //  NFC 支付宝碰一下激活
                ActiveDeviceDTO activeDeviceDTO = new ActiveDeviceDTO().setTerminalId(String.valueOf(deviceId))
                        .setDeviceName(deviceName)
                        .setBoardSn(nfcSn).setNfcSn(nfcSn).setAddress(dv.getPlaceName());
                Boolean b = R.feignCheckData(alipayDeviceService.nfcDeviceActive(activeDeviceDTO));
                if (BooleanUtil.isFalse(b)) {
                    throw new RuntimeException("NFC设备激活失败!");
                }
            }
            //过滤需要付费的设备
            if (cover.containsKey(deviceId)) {
                return;
            }
            DeviceInfo updateDeviceInfo = new DeviceInfo()
                    .setDeviceId(deviceId)
                    .setActiveState(activeState)
                    .setBusyState(busyState)
                    .setActiveTime(now)
                    .setShowStatus(true);
            activeList.add(updateDeviceInfo);
        });
        if (Emptys.check(activeList)) {
            deviceInfoService.updateBatchById(activeList);
        }
        return R.ok(result);
    }

    @Override
    public R restActive(MercMiniDeviceDto.RestActive restActive) {
        Long deviceId = restActive.getDeviceId();
        String nfcSn = restActive.getNfcSn();
        //nfc解绑重置
        if (StrUtil.isNotEmpty(nfcSn)) {
            String loginId = AuthorizeUtils.getLoginId(String.class);
            ResetDeviceDTO resetParams = new ResetDeviceDTO().setOperatorId(loginId).setOperatorName(loginId).setRemark("设备激活重置").setTerminalId(String.valueOf(deviceId));
            alipayDeviceService.nfcDeviceReset(resetParams);
            R.feignCheckData(alipayDeviceService.nfcDeviceReset(resetParams));
            deviceSysinfoMapper.updateById(new DeviceSysinfo().setDeviceId(deviceId).setNfcSn(StrUtil.EMPTY));
        }
        deviceChargingService.removeById(deviceId);
        deviceAlgorithmChargingService.remove(new LambdaQueryWrapper<DeviceAlgorithmCharging>()
                .eq(DeviceAlgorithmCharging::getDeviceId, restActive.getDeviceId())
        );
        deviceInfoService.update(new LambdaUpdateWrapper<DeviceInfo>()
                .set(DeviceInfo::getActiveState, 2)
                .set(DeviceInfo::getActiveTime, null)
                .eq(DeviceInfo::getDeviceId, restActive.getDeviceId())
        );

        return R.ok();
    }

    @Override
    public R<List<Tuple.Tuple3<Long, Long, Boolean>>> senCommand(List<CommandMqtt> commandMqtts) {
        //发送指令
        List<MqttCmdDto> mqttDtos = new ArrayList<>();
        commandMqtts.forEach(commandMqtt -> {
            JSONObject templetObj = commandMqtt.getTemplet();
            String debug = templetObj.getStr(LambdaUtils.getProperty(MqttCmdDto::getDebug));
            String level = templetObj.getStr(LambdaUtils.getProperty(MqttCmdDto::getLevel));
            String delayTime = templetObj.getStr(LambdaUtils.getProperty(MqttCmdDto::getDelayTime));
            String timeout = templetObj.getStr(LambdaUtils.getProperty(MqttCmdDto::getTimeout));
            String wkSn = templetObj.getStr(LambdaUtils.getProperty(PaterDto::getWkSn));
            String actionType = templetObj.getStr(LambdaUtils.getProperty(PaterDto::getActionType));
            String cmdType = templetObj.getStr(LambdaUtils.getProperty(PaterDto::getCmdType));
            String ack = templetObj.getStr(LambdaUtils.getProperty(PaterDto::getAck));
            JSONObject data = templetObj.getJSONObject(LambdaUtils.getProperty(PaterDto::getData));
            PaterDto paterDto = new PaterDto()
                    .setActionType(actionType)
                    .setCmdType(cmdType)
                    .setAck(Emptys.check(ack) && !ack.equals("$ack$") ? Boolean.valueOf(ack) : Emptys.check(ack) && ack.equals("$ack$") ? true : true)
                    .setWkSn(!Emptys.check(wkSn) ? mqttService.getWkSn() : "$wkSn$".equals(wkSn) ? mqttService.getWkSn() : Long.valueOf(wkSn))
                    .setData(data);
            MqttCmdDto mqttDto = new MqttCmdDto()
                    .setDeviceId(commandMqtt.getDeviceId())
                    .setDebug(Emptys.check(debug) && !debug.equals("$debug$") ? Boolean.valueOf(debug) : Emptys.check(debug) && debug.equals("$debug$") ? false : false)
                    .setLevel(Emptys.check(level) && !level.equals("$level$") ? Integer.valueOf(level) : Emptys.check(level) && level.equals("$level$") ? 1 : 1)
                    .setDelayTime(Emptys.check(delayTime) && !delayTime.equals("$delayTime$") ? delayTime : Emptys.check(delayTime) && delayTime.equals("$delayTime$") ? null : null)
                    .setTimeout(Emptys.check(timeout) && !timeout.equals("$timeout$") ? Integer.valueOf(timeout) : Emptys.check(timeout) && timeout.equals("$timeout$") ? 60 : 60)
                    .setPater(paterDto);
            mqttDtos.add(mqttDto);
        });
        List<Tuple.Tuple3<Long, Long, Boolean>> list = mqttService.sendMqtt(mqttDtos);
        //执行业务
        Map<String, List<MqttCmdDto>> map = new HashMap<>();
        mqttDtos.forEach(mqttDto -> {
            String cmdType = mqttDto.getPater().getCmdType();
            if (!map.containsKey(cmdType)) {
                map.put(cmdType, new ArrayList<>());
            }
            map.get(cmdType).add(mqttDto);
        });
        map.forEach((k, v) -> {
            CmdService service = FactoryUtils.getService(CmdService.class, k);
            if (service == null) {
                return;
            }
            service.senCommand(v);
        });
        return R.ok(list);
    }

    @Override
    public R netRecord(DeviceNetRecordDto.Save save) {
        return R.ok();
    }

    @Override
    public R eventMsg(DeviceEventMsgDto.Save save) {
        DeviceInfoDto.Vo deviceInfo = deviceInfoService.obj(new DeviceInfoDto.Obj().setDeviceId(save.getDeviceId()).setIsSysinfo(true)).getData();
        //查询消息编码信息
        SysCodeConfigureRedis sysCodeConfigureRedis = SysCodeConfigureUtils.get(save.getCode());
        //添加消息记录
        save.setMercId(deviceInfo.getMercId());
        DeviceEventMsg deviceEventMsg = copy(DeviceEventMsg.class, save)
                .setCreateTime(LocalDateTime.now());
        deviceEventMsgService.save(deviceEventMsg);
        //异常消息，修改设备当前故障等级
        if (Emptys.check(sysCodeConfigureRedis) && Emptys.check(sysCodeConfigureRedis.getPaterCode())) {
            if (sysCodeConfigureRedis.getPaterCode().equals(SysCodeConfigureEnum.D01.getCode())) {
                if (Emptys.check(sysCodeConfigureRedis.getExpand())) {
                    DeviceInfo updateDeviceInfo = new DeviceInfo()
                            .setDeviceId(deviceInfo.getDeviceId())
                            .setFaultLevel(1);
                    deviceInfoService.updateById(updateDeviceInfo);
                    //设备故障消息推送
                    sendMsg(deviceEventMsg);
                }
            }
            ArrayList<String> arrayList = CollUtil.newArrayList("APP", "BLT", "DEV", "ERR", "MQTT", "SHJ", "SYS");
            if (sysCodeConfigureRedis.getPaterCode().equals(SysCodeConfigureEnum.M01.getCode())
                    || arrayList.contains(sysCodeConfigureRedis.getPaterCode())) {
                //设备消息消息推送
                sendMsg(deviceEventMsg);
            }
        }
        return R.ok();
    }

    /**
     * 设备事件推送
     *
     * @param deviceEventMsg 设备事件味精
     */
    private void sendMsg(DeviceEventMsg deviceEventMsg) {
        String code = deviceEventMsg.getCode();
        if (StrUtil.isEmpty(code)) {
            return;
        }
        deviceMqttSendService.devicePushMsg(new MqttDto.RequestParams().setData(JSONUtil.toJsonStr(deviceEventMsg)));
        //设备故障监控
        DeviceFaultMonitorConfigDto.SelectList selectList = new DeviceFaultMonitorConfigDto.SelectList();
        //启用的配置
        selectList.setState(true);
        //监控的事件
        selectList.setEventCode(code);
        //监控配置
        List<DeviceFaultMonitorConfigDto.Vo> voList = R.feignCheckData(deviceFaultMonitorConfigService.list(selectList));
        if (CollUtil.isNotEmpty(voList)) {
            //存在则发送到mqtt处理
            DeviceFaultLogDto.MqttData mqttData = new DeviceFaultLogDto.MqttData().setConfigList(voList).setDeviceEventMsg(BeanUtil.copyProperties(deviceEventMsg, DeviceEventMsgDto.Vo.class));
            deviceMqttSendService.deviceFault(new MqttDto.RequestParams().setData(JSONUtil.toJsonStr(mqttData)));
        }
    }

    @Override
    public R busyStatus(MercMiniDeviceDto.BusySate busySate) {
        DeviceInfo updateDeviceInfo = new DeviceInfo()
                .setDeviceId(busySate.getDeviceId())
                .setMercId(busySate.getMercId())
                .setBusyState(busySate.getBusyState());
        deviceInfoService.updateById(updateDeviceInfo);
        //发布事件
        DeviceEvent.BusyStateEvent busyStateEvent = new DeviceEvent.BusyStateEvent()
                .setBusyState(busySate.getBusyState());
        busyStateEvent.setValue(busySate.getDeviceId());
        EventUtils.push(() -> busyStateEvent);
        return R.ok();
    }

}
