package net.northking.cctp.device.api.pcDevice;

import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.device.config.PrometheusConfig;
import net.northking.cctp.device.constants.DeviceConstants;
import net.northking.cctp.device.constants.DeviceError;
import net.northking.cctp.device.db.entity.CdDeviceStatus;
import net.northking.cctp.device.db.entity.CdPcDevice;
import net.northking.cctp.device.db.service.CdDeviceStatusService;
import net.northking.cctp.device.db.service.CdPcDeviceService;
import net.northking.cctp.device.schedule.MonitorDataSchedule;
import net.northking.cctp.device.util.prometheus.PromSqlDto;
import net.northking.cctp.device.util.prometheus.PrometheusClient;
import net.northking.cctp.device.util.prometheus.PrometheusTargets;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint(value = "/pub/device/pc/monitor-data/{uuid}")
public class PcMonitorDataWS {

    private Session session;

    private static CopyOnWriteArraySet<PcMonitorDataWS> socketList = new CopyOnWriteArraySet<>();

    private static HashMap<String, PromSqlDto> map = new HashMap<>();

    private static Scheduler scheduler;

    private final static String SCHEDULER_JOB_KEY = "PERFORMANCE_INFO";

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

    private static CdPcDeviceService cdPcDeviceService;

    private static CdDeviceStatusService cdDeviceStatusService;

    private static PrometheusConfig prometheusConfig;

    private int amount = 5;

    private long step = 15L;

    public int getAmount() {
        return amount;
    }

    public long getStep() {
        return step;
    }

    @Autowired
    public void setPrometheusConfig(PrometheusConfig prometheusConfig){
        PcMonitorDataWS.prometheusConfig = prometheusConfig;
    }

    @Autowired
    public void setCdPcDeviceService(CdPcDeviceService cdPcDeviceService) {
        PcMonitorDataWS.cdPcDeviceService = cdPcDeviceService;
    }

    @Autowired
    public void setCdPcDeviceService(CdDeviceStatusService cdDeviceStatusService) {
        PcMonitorDataWS.cdDeviceStatusService = cdDeviceStatusService;
    }


    @Autowired
    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    @OnOpen
    public void OnOpen(Session session, @PathParam("uuid") String uuid) throws Exception {
        try {
            logger.debug("websocket创建成功，{}", uuid);
            CdDeviceStatus status = cdDeviceStatusService.findByPrimaryKey(uuid);
            //设备是否处于在线状态
            if (!status.getDeviceStatus().equals("1")) {
                session.getBasicRemote().sendText(DeviceConstants.STR_ERROR);
                throw new PlatformRuntimeException(DeviceError.DEVICE_NOT_ONLINE);
            }
            //查询设备的jobName 和 instance
            CdPcDevice device = cdPcDeviceService.findByPrimaryKey(uuid);
            if (device!=null) {
                if (device.getJobName() == null || device.getInstance() == null) {
                    //从prometheus中获取数据
                    PrometheusTargets targets = PrometheusClient.queryTargets(prometheusConfig.getUrl());
                    int check = 0;
                    for (PrometheusTargets.ActiveTargets one : targets.getData().getActiveTargets()) {
                        PrometheusTargets.DiscoveredLabels label = one.getDiscoveredLabels();
                        PrometheusTargets.Labels labels = one.getLabels();
                        if (label.get__address__().equals(device.getIpAddr()+":"+device.getPromPort())) {
                            device.setInstance(labels.getInstance());
                            device.setJobName(labels.getJob());
                            check = DeviceConstants.WS_DOWN.equals(one.getHealth()) ? 2 : 1;
                            break;
                        }
                    }
                    if (check == 0) {
                        session.getBasicRemote().sendText(DeviceConstants.STR_ERROR);
                        throw new PlatformRuntimeException(DeviceError.NO_EXIST_HOST);
                    } else if (check == 2) {
                        session.getBasicRemote().sendText(DeviceConstants.STR_ERROR);
                        throw new PlatformRuntimeException(DeviceError.MONITOR_TARGET_NO_RUNNING);
                    }
                    cdPcDeviceService.updateByPrimaryKey(device);
                }
            }else {
                //如果设备id不存在 返回为空
                session.getBasicRemote().sendText(DeviceConstants.STR_ERROR);
                throw new PlatformRuntimeException(DeviceError.DATA_ERROR);
            }

            PromSqlDto dto = new PromSqlDto();
            dto.setInstance(device.getInstance());
            dto.setJobName(device.getJobName());
            dto.setSession(session);

            this.session = session;
            socketList.add(this);
            //
            map.put(session.getId(), dto);
            //创建定时任务
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(DeviceConstants.WS_MAP_LIST, map);
            dataMap.put(DeviceConstants.WS_SOCKET_LIST, socketList);
            //当前设备id
            boolean exists = scheduler.checkExists(JobKey.jobKey(SCHEDULER_JOB_KEY));
            if (!exists) {
                JobDetail detail = JobBuilder.newJob(MonitorDataSchedule.class)
                        .withIdentity(SCHEDULER_JOB_KEY)
                        .usingJobData(dataMap)
                        .build();
                CronTrigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(SCHEDULER_JOB_KEY)
                        .withSchedule(CronScheduleBuilder.cronSchedule(DeviceConstants.WS_CORN))
                        .build();
                scheduler.scheduleJob(detail, trigger);
            }
        } catch (Exception e) {
            OnClose(session);
            logger.error("socket建立连接异常：", e);
        }
    }

    @OnMessage
    public void onMessage(Session session, String data) throws Exception {
        JSONObject object = JSONObject.parseObject(data);
        boolean checkAmount = object.containsKey(DeviceConstants.WS_AMOUNT);
        if (checkAmount){
            amount = Integer.parseInt(object.get(DeviceConstants.WS_AMOUNT).toString());
        }
        boolean checkStep = object.containsKey(DeviceConstants.WS_STEP);
        if (checkStep){
            step = Long.parseLong(object.get(DeviceConstants.WS_STEP).toString()) ;
        }
    }



    @OnClose
    public void OnClose(Session session) throws Exception {
        socketList.remove(this);
        JobDataMap dataMap = scheduler.getJobDetail(JobKey.jobKey(SCHEDULER_JOB_KEY)).getJobDataMap();
        logger.info("链接【{}】断开连接，剩下还有【{}】在线连接", session.getId(), socketList.size());
        if (socketList.size() == 0) {
            boolean exists = scheduler.checkExists(JobKey.jobKey(SCHEDULER_JOB_KEY));
            if (exists) {
                scheduler.pauseJob(JobKey.jobKey(SCHEDULER_JOB_KEY));
                scheduler.pauseTrigger(TriggerKey.triggerKey(SCHEDULER_JOB_KEY));
                scheduler.deleteJob(JobKey.jobKey(SCHEDULER_JOB_KEY));
                logger.info("定时任务停止");
            }
        }
        session.close();
    }


    public Session getSession() {
        return session;
    }
}
