package com.qiuguo.iot.box.websocket.api.handler;

import com.alibaba.fastjson.JSONObject;
import com.qiuguo.iot.base.enums.AskTypeEnum;
import com.qiuguo.iot.base.enums.RespCodeEnum;
import com.qiuguo.iot.base.utils.StringUtils;
import com.qiuguo.iot.box.websocket.api.domain.BaseSession;
import com.qiuguo.iot.box.websocket.api.domain.box.resp.BoxMessageResp;
import com.qiuguo.iot.box.websocket.api.domain.user.UserTalkMessage;
import com.qiuguo.iot.box.websocket.api.filter.LogMdcConfiguration;
import com.qiuguo.iot.data.entity.device.DeviceUserBindEntity;
import com.qiuguo.iot.data.entity.device.DeviceUserTalkRecordEntity;
import com.qiuguo.iot.data.entity.system.SystemTalkBindDeviceEntity;
import com.qiuguo.iot.data.request.device.DeviceUserBindRequest;
import com.qiuguo.iot.data.request.system.SystemTalkBindDeviceRequest;
import com.qiuguo.iot.data.request.third.ThirdWeatherInfoRequest;
import com.qiuguo.iot.data.resp.third.weather.TianqiapiItemResp;
import com.qiuguo.iot.data.service.device.DeviceUserBindService;
import com.qiuguo.iot.data.service.device.DeviceUserTalkRecordService;
import com.qiuguo.iot.data.service.system.SystemAddressService;
import com.qiuguo.iot.data.service.system.SystemTalkBindDeviceService;
import com.qiuguo.iot.third.nlp.action.Action;
import com.qiuguo.iot.third.nlp.action.Actions;
import com.qiuguo.iot.third.query.TuyaQuery;
import com.qiuguo.iot.third.service.NlpService;
import com.qiuguo.iot.third.service.TuyaDeviceService;
import com.qiuguo.iot.third.service.WeatherService;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class BaseWebSocketProcess {


    @Resource
    protected DeviceUserBindService deviceUserBindService;


    @Autowired
    protected TuyaDeviceService tuyaDeviceService;

    @Resource
    protected SystemTalkBindDeviceService systemTalkBindDeviceService;

    @Autowired
    protected WeatherService weatherService;

    @Autowired
    protected SystemAddressService systemAddressService;

    @Autowired
    protected DeviceUserTalkRecordService deviceUserTalkRecordService;


    protected static ConcurrentHashMap<Long, BaseSession> userGroup = new ConcurrentHashMap<>();

    protected static ConcurrentHashMap<String, BaseSession> boxGroup = new ConcurrentHashMap<>();

    protected static String apiType = "api-type";
    protected static String apiToken = "api-token";

    protected void processAction(Actions actions, Long userId, BaseSession baseSession) {
        //目前只处理第一条动作
        Action action = actions.getActions().get(0);


        log.info("匹配到自定义指令{}", action.getSystemTalkAnswerConfigEntity());
        if(action.getSystemTalkAnswerConfigEntity() == null){
            log.info("调用千问");
            sendMessage(action, baseSession, 0, "暂时无法理解，我还在努力学习中");
        }
        else if(action.getSystemTalkAnswerConfigEntity().getAnswerType().equals(AskTypeEnum.IOT.getCode())){

            if(action.getName().size() > 0){
                String deviceName = action.getName().get(0);
                DeviceUserBindRequest deviceUserBindRequest = new DeviceUserBindRequest();
                deviceUserBindRequest.setUserId(userId);
                deviceUserBindRequest.setPageSize(2);
                deviceUserBindRequest.setBindName(deviceName);

                //查询是否有相关设备
                deviceUserBindService.selectDeviceUserBindsByRequest(deviceUserBindRequest)
                        .defaultIfEmpty(new PagerResult<>(0, null))
                        .map(binds ->{
                            if(binds.getTotal() == 0){
                                //返回告诉没有备
                                sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), "未找到相关设备，无法操做！");
                            }else if(binds.getTotal() > 1){
                                //返回告诉有多个设备，请详细说明具体说明设备
                                sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), "您有多个相同设备，请明确说明");
                            }else{
                                //查询是否有相关指令绑定
                                DeviceUserBindEntity deviceUserBindEntity = binds.getData().get(0);
                                SystemTalkBindDeviceRequest systemTalkBindDeviceRequest = new SystemTalkBindDeviceRequest();
                                systemTalkBindDeviceRequest.setCategoryCode(deviceUserBindEntity.getCategoryCode());
                                systemTalkBindDeviceRequest.setSystemTalkId(action.getSystemTalkAnswerConfigEntity().getId());
                                systemTalkBindDeviceService.selectSystemTalkBindDeviceByRequest(systemTalkBindDeviceRequest)
                                        .defaultIfEmpty(new SystemTalkBindDeviceEntity())
                                        .map(systemTalkBindDeviceEntity -> {
                                            if(systemTalkBindDeviceEntity.getId() == null){
                                                //通知不支持的指令
                                                sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), deviceName + "不支持" + action.getAction() + "指令！");

                                            }else{
                                                //调用涂鸦
                                                TuyaQuery query = new TuyaQuery();
                                                query.setDeviceId(deviceUserBindEntity.getOtherDeviceId());
                                                query.setValue(action.getStatus());
                                                query.setUserHandlingDeviceId(systemTalkBindDeviceEntity.getUserHandlingId());
                                                tuyaDeviceService.controlDevice(query).map(isOk ->{
                                                    String msg = "";
                                                    if(isOk.getCode().equals(RespCodeEnum.SUCESS.getCode())){
                                                        //通知打开灯成功
                                                        msg = action.getSystemTalkAnswerConfigEntity().getAnswerValue().replaceAll("#name#", deviceName);
                                                        if(StringUtils.isNotEmpty(action.getStatus())){
                                                            msg = msg.replace("#value#", action.getStatus());
                                                        }

                                                        log.info("执行指令");
                                                    }else{
                                                        //通知开灯失败;
                                                        msg = action.getSystemTalkAnswerConfigEntity().getAnswerValueFaild().replaceAll("#name#", deviceName);
                                                        log.info("执行指令失败");
                                                    }
                                                    sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), msg);

                                                    return isOk;
                                                }).subscribe();
                                            }
                                            return systemTalkBindDeviceEntity;
                                        }).subscribe();

                            }
                            return Mono.empty();
                        }).subscribe();
            }else{
                sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), "未找到对应的设备");
            }

        }else if(action.getSystemTalkAnswerConfigEntity().getAnswerType().equals(AskTypeEnum.WEATHER.getCode())){
            ThirdWeatherInfoRequest req = new ThirdWeatherInfoRequest();
            //String city = "";
            if(action.getLbs() != null && action.getLbs().size() > 0){
                //根据地址查询天气
               // city = ;
                req.setCity(action.getLbs().get(action.getLbs().size() - 1));
            }else{
                //使用IP查询天气
                req.setIp("115.205.2.137");
            }
            weatherService.tianqiApi(req).map(t ->{
                log.info("查询的天气{}", JSONObject.toJSONString(t));
                TianqiapiItemResp item = null;

                if(StringUtils.isNotEmpty(action.getTime().getDateTime())){
                    //匹配对应的日期
                    for (TianqiapiItemResp itemResp : t.getData())
                          {
                              if(action.getTime().getDateTime().equals(itemResp.getDate())){
                                  item = itemResp;
                                  break;
                              }
                    }

                }else{
                    item = t.getData().get(0);
                }
                String msg = "";
                if(item != null){
                    //返回给客户端播报内容
                    msg = t.getCity() + action.getTime().getTime() + "天气"
                            + item.getNarrative().replace("每 km / h", "千米每小时")
                            + "，空气质量" + item.getAir_level()
                            + "，湿度" + item.getHumidity() + "，最低气温" + item.getTem2();

                }else{
                    msg = action.getSystemTalkAnswerConfigEntity().getAnswerValueFaild();

                    log.info("执行指令失败");
                }
                sendMessage(action, baseSession, action.getSystemTalkAnswerConfigEntity().getAnswerType(), msg);
                return t;
            }).subscribe();
        }

    }

    /*protected Mono<Void> sessionProcess(WebSocketSession session, Long userId, String ip, int type) {
        Mono<Void> input = session.receive().map(webSocketMessage ->{
            //MDC.put(LogMdcConfiguration.PRINT_LOG_ID, getBoxSessionWithSn().getLogId());
            String text = webSocketMessage.getPayloadAsText();
            log.info("收到消息：{}", text);
            UserTalkMessage userTalkMessage = JSONObject.parseObject(text, UserTalkMessage.class);
            nlpService.getActionWithLacSingle(userTalkMessage.getMessage()).defaultIfEmpty(new Actions()).map(actions -> {
                BaseSession baseSession  = null;
                if(type == 0){

                    baseSession  = getUserSessionWithUserId(userTalkMessage.getUserId());
                }else{
                    baseSession  = getUserSessionWithUserId(userTalkMessage.getUserId());
                }

                //处理
                if(actions.getActions() == null || actions.getActions().size() == 0){
                    //调用千问回答
                    log.info("未匹配到自定义命令，调用千问");
                }else if(baseSession == null){
                    log.info("未匹配到用户session，可能传错用户id");
                    session.close().subscribe();
                }else{
                    processAction(actions, userId, baseSession);
                }

                return Mono.empty();
            }).subscribe();
            log.info("收到用户userId:{}，消息：{}", userTalkMessage.getUserId(), userTalkMessage.getMessage());
            //MDC.remove(LogMdcConfiguration.PRINT_LOG_ID);
            return Mono.empty();
        }).then();
        BaseSession userSession = new BaseSession();
        userSession.setUserId(userId);
        userSession.setSession(session);
        userSession.setCustomerIP(ip);
        userSession.setLogId(MDC.get(LogMdcConfiguration.PRINT_LOG_ID));
        userGroup.put(userId, userSession);
        Mono<Void> output = session.send(Flux.create(sink -> userSession.setSink(sink))).then();

//          Mono.zip() 会将多个 Mono 合并为一个新的 Mono，任何一个 Mono 产生 error 或 complete 都会导致合并后的 Mono
//          也随之产生 error 或 complete，此时其它的 Mono 则会被执行取消操作。

        return Mono.zip(input, output).doFinally(signalType -> {
            // MDC.put(LogMdcConfiguration.PRINT_LOG_ID, requestId);
            userGroup.remove(userSession.getUserId());//断链后及时移除
            log.info("断开连接SN：{}", userSession.getUserId());
            //  MDC.remove(LogMdcConfiguration.PRINT_LOG_ID);
        }).then();
    }*/

    private void sendMessage(Action action, BaseSession baseSession, Integer type, String message){
        BoxMessageResp resp = new BoxMessageResp();
        resp.setType(type);
        resp.setText(message);
        DeviceUserTalkRecordEntity talkRecord = new DeviceUserTalkRecordEntity();
        talkRecord.setAskType(type);
        talkRecord.setAskValue(action.getAsk());
        talkRecord.setAskKey(action.getAction());
        talkRecord.setAnswerValue(message);
        talkRecord.setUserId(baseSession.getUserId());
        talkRecord.setDeviceId(baseSession.getDeviceId());
        if(type.equals(AskTypeEnum.MUSIC.getCode())) {
            if(this instanceof CustomerWebSocketHandler) {
                log.info("推送Box播放音乐");
                BaseSession boxSession = getBoxSessionWithSn(baseSession.getSn());
                if(boxSession != null){
                    boxSession.getSink().next(baseSession.getSession().textMessage(JSONObject.toJSONString(resp)));
                }else{
                    log.info("设备sn：{}不在线，无法播放", baseSession.getSn());
                    resp.setText("设备不在线，无法播放");
                }
            }
        }
        deviceUserTalkRecordService.insertDeviceUserTalkRecord(talkRecord).map(i ->{
            if(this instanceof BoxWebSocketHandler){
                log.info("推送通知到客户端");
                BaseSession userSession = getUserSessionWithUserId(baseSession.getUserId());
                if(userSession != null){
                    userSession.getSink().next(baseSession.getSession().textMessage(JSONObject.toJSONString(resp)));
                }
            }
            baseSession.getSink().next(baseSession.getSession().textMessage(JSONObject.toJSONString(resp)));

            return Mono.empty();
        }).subscribe();//保存聊天记录

    }

    public BaseSession getBoxSessionWithSn(String sn) {
        if(boxGroup.containsKey(sn)){
            return boxGroup.get(sn);
        }
        return null;
    }

    public BaseSession getUserSessionWithUserId(Long userId) {
        if(userGroup.containsKey(userId)){
            return userGroup.get(userId);
        }
        return null;
    }
}
