package net.northking.cctp.mobile.db.impl;

import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.mobile.constants.ErrorMessage;
import net.northking.cctp.mobile.db.service.MobileConnectService;
import net.northking.cctp.mobile.dto.device.CdDeviceCtrl;
import net.northking.cctp.mobile.dto.device.DeviceInfoDto;
import net.northking.cctp.mobile.dto.device.DeviceUiTreeDto;
import net.northking.cctp.mobile.entity.WSRequest;
import net.northking.cctp.mobile.enums.PlatformType;
import net.northking.cctp.mobile.feign.device.DeviceMgrFeignClient;
import net.northking.cctp.mobile.thread.ProcessMsgThread;
import net.northking.cctp.mobile.util.HttpUtils;
import net.northking.cctp.mobile.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.websocket.CloseReason;
import javax.websocket.Session;
import java.io.OutputStream;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Service
public class MobileConnectServiceImpl implements MobileConnectService {

    private static final Logger logger = LoggerFactory.getLogger(MobileConnectServiceImpl.class);

    @Autowired
    private ThreadPoolExecutor executorService;

    /**
     * web端到本服务的会话集合 deviceId-Session
     */
    private ConcurrentMap<String,Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 处理每一个真机连接指令的任务
     */
    private ConcurrentHashMap<String, Future> mobileConnectionTaskMap = new ConcurrentHashMap<>();

    /**
     * 处理每一个真机连接指令操作线程
     */
    private ConcurrentHashMap<String, ProcessMsgThread> mobileConnectionThreadMap = new ConcurrentHashMap<>();

    /**
     *  缓存用户token的map
     */
    private static Map<String, String> userTokenMap = new ConcurrentHashMap<>();


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMgrFeignClient deviceMgrFeignClient;


    //上位机ws地址
    private final static String TMPL_UPPER_WS_URI = "ws://%s:%s/connectMobile?serial=%s&type=%s";

    //uitree获取地址
    private final static String UI_TREE_PATH = "http://%s:%s/element/getUiTree?deviceId=%s&type=%s";


    /**
     * 根据设备token获取设备id
     * @param deviceToken
     * @return
     */
    @Override
    public String getDeviceIdByToken(String deviceToken) {
        ResultWrapper<DeviceInfoDto> wrapper = null;
        try {
            wrapper = deviceMgrFeignClient.getDeviceInfoByToken(deviceToken);
            if (wrapper.isSuccess() && null != wrapper.getData()) {
                DeviceInfoDto deviceInfoDto = wrapper.getData();
                if (null != deviceInfoDto.getCdMobileDevice()) {
                    return deviceInfoDto.getCdMobileDevice().getId();
                }
            }
        }catch (Exception e) {
            logger.error("根据设备token获取设备信息异常",e);
        }
        return null;
    }

    @Override
    public String getUiTree(DeviceUiTreeDto dto) {
        if (dto == null) {
            throw new PlatformRuntimeException(new ErrorMessage("设备信息不能为空"));
        }
        String deviceId = dto.getDeviceId();
        String type = dto.getType();
        String res = null;
        if (StringUtils.hasText(deviceId)) {
            ResultWrapper<DeviceInfoDto> wrapper = deviceMgrFeignClient.getLock(deviceId);
            if (wrapper.isSuccess() && null != wrapper.getData()) {
                CdDeviceCtrl deviceCtrl = wrapper.getData().getCdDeviceCtrl();
                String ctrlIp = deviceCtrl.getCtrlIp();
                String ctrlPort = deviceCtrl.getCtrlPort();
                if (StringUtils.hasText(ctrlIp) && StringUtils.hasText(ctrlPort)) {
                    String plat = PlatformType.ANDROID.getName();
                    if (PlatformType.IOS.getCode().equals(type) || type.equalsIgnoreCase(PlatformType.IOS.getName())) {
                        plat = PlatformType.IOS.getName();
                    }
                    String urlInfo = String.format(UI_TREE_PATH, ctrlIp, ctrlPort,deviceId,plat);
                    try {
                        ResultWrapper<String> upperWrapper = HttpUtils.doGet(new URI(urlInfo), null, ResultWrapper.class);
                        if (upperWrapper.isSuccess()) {
                            res = upperWrapper.getData();
                            logger.debug("获取ui树成功");
                        }else {
                            logger.warn("获取ui树失败，原因为："+upperWrapper.getMessage());
                            throw new PlatformRuntimeException(new ErrorMessage("从上位机获取xml树失败，原因为："+upperWrapper.getMessage()));
                        }
                    } catch (Exception e) {
                        logger.error("获取ui树异常，原因为：",e);
                        throw new PlatformRuntimeException(new ErrorMessage("从上位机获取xml树异常"));
                    }
                }
            }
        }
        return res;
    }

    @Override
    public void closeMobileConnection(String deviceId) {
        ProcessMsgThread processMsgThread = mobileConnectionThreadMap.remove(deviceId);
        if (processMsgThread != null) {
            logger.warn("移除设备【{}】与前端的连接.........", deviceId);
            processMsgThread.cleanDataInExit();
        } else {
            logger.warn("设备【{}】与前端的连接不存在.........", deviceId);
        }
        Future futureTask = mobileConnectionTaskMap.remove(deviceId);
        if (futureTask != null) {
            futureTask.cancel(true);
            logger.warn("设备【{}】任务已经取消.........", deviceId);
        } else {
            logger.warn("设备【{}】任务不存在.........", deviceId);
        }
        logger.info("还活跃的线程：{}",executorService.getActiveCount());
    }

    @Override
    public void releaseUsingDevice(String deviceId, String deviceToken) {
        //释放设备
        logger.debug("释放设备token:{} ", deviceToken);
        ResultWrapper<Boolean> wrapper = deviceMgrFeignClient.deviceRelease(deviceToken);
        if (wrapper.isSuccess() && wrapper.getData()) {
            //释放成功，更新报告
            logger.debug("释放设备【{}】成功",deviceId);
            //释放设备成功后，删除报告相关设备信息
//            redisTemplate.delete(DeviceConstants.DEVICE_REPORT_INFO_PRE_KEY + deviceId);
            //todo:失败怎么说？
        } else {
            logger.warn("释放设备失败: " + wrapper.getMessage());
        }
    }


    @Override
    public void prepareConnectionEnvironment(String sessionId, Session session, String deviceToken, String deviceId, String userToken,String mode) {
        logger.info("即将准备手机的连接，sessionId:{},deviceId:{}",sessionId,deviceId);
        int activeCount = executorService.getActiveCount();
        int corePoolSize = executorService.getCorePoolSize();
        logger.info("活跃的线程：{}，线程池最大支持线程:{}",activeCount,corePoolSize);
        if (activeCount < corePoolSize) {
            logger.info("线程池还能支持手机连接.........................");
            Future future = mobileConnectionTaskMap.get(deviceId);
            if (future == null || future.isCancelled() || future.isDone()) {
                ProcessMsgThread processMsgThread = new ProcessMsgThread(userToken, session,sessionId, deviceId, deviceToken, mode);
                future = executorService.submit(processMsgThread);
                mobileConnectionThreadMap.put(deviceId, processMsgThread);
                mobileConnectionTaskMap.put(deviceId, future);
            }else {
                logger.warn("设备【{}】消费web端消息的线程已经存在，可以继续使用。。。。。。。", deviceId);
            }
        } else {
            logger.info("手机连接的线程池已经满了............");
            throw new PlatformRuntimeException(new ErrorMessage(50000,"目前连接设备过多，请稍后再试"));
        }
    }

    @Override
    public void processWebMsg(String msg, String deviceId) {
        if (msg.equals("ping")) {
            logger.debug("receive a ping-----");
            return;
        }
        logger.debug("web msg:{}",msg);
        WSRequest request = JsonUtils.fromJsonString(msg, WSRequest.class);
        String sessionId = request.getSessionId();
        if (StringUtils.hasText(sessionId) && StringUtils.hasText(deviceId)) {
            ProcessMsgThread processMsgThread = mobileConnectionThreadMap.get(deviceId);
            processMsgThread.addMsg(request);
            logger.debug("processMsgThread addMsg...");
        } else {
            logger.warn("出现没有sessionId的请求。。。。。。。。。。");
        }
    }

    @Override
    public Session getSession(String deviceId) {
        return sessionMap.get(deviceId);
    }

    @Override
    public void addUserToken(String sessionId, String token) {
        userTokenMap.put(sessionId, token);
    }

    @Override
    public void removeUserToken(String id) {
        userTokenMap.remove(id);
    }
}
