package com.tsi.monitoring.webSocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tsi.monitoring.repository.GlobalStatusInfoRepository;
import com.tsi.monitoring.service.SectionService;
import com.tsi.monitoring.service.UtisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.net.http.WebSocketHandshakeException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
@Component
@EnableScheduling
public class MonitoringWebSocketHandler extends TextWebSocketHandler {
    private static final CopyOnWriteArrayList<WebSocketSession> sessionList = new CopyOnWriteArrayList<>();
    private final ObjectMapper om = new ObjectMapper();
    private final GlobalStatusInfoRepository repository;
    private final UtisService utisService;
    private final SectionService sectionService;

    public MonitoringWebSocketHandler(GlobalStatusInfoRepository repository, UtisService utisService, SectionService sectionService) {
        this.repository = repository;
        this.utisService = utisService;
        this.sectionService = sectionService;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception, WebSocketHandshakeException {
        log.info("afterConnectionEstablished: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
        sessionList.add(session);
        // 연결 직후 초기 데이터 전송
        sendMonitoringMessage(session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception, WebSocketHandshakeException {
        log.info("handleTextMessage: " + session.getRemoteAddress() + ",  Uri[" + session.getUri() + "], UUID[" + session.getId() + "], message[" + message.getPayload() + "]");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) throws Exception, WebSocketHandshakeException {
        log.info("afterConnectionClosed: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
        sessionList.remove(session);
    }

    // 10초마다 전체 세션에 메시지 전송
    @Scheduled(fixedRate = 10000)
    public void sendMonitoringMessage() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        int count = 0;

        try {
            this.utisService.setProcessStatusValue();
            this.utisService.setAllProcessStatusValue();
            this.sectionService.setLocalCenterLinkInfo();
            String monitoringMsg = getMonitoringMessage();

            for (WebSocketSession session : sessionList) {
                if (session.isOpen()) {
                    session.sendMessage(new TextMessage(monitoringMsg));
                    count++;
                }
            }
//            log.info("Send Monitoring Data Message to {} clients", count);
        }
        catch (JsonProcessingException e ) {
            log.error("Error Send Monitoring Data Message : JsonProcessingException");
        } catch (IOException e) {
            log.error("Error Send Monitoring Data Message : IOException");
        }
        stopWatch.stop();
//        log.info("sendMonitoringMessage :--END. {}, {} ms. Client Count {}", Thread.currentThread().getName(), stopWatch.getTotalTimeMillis(), count);
    }

    private void sendMonitoringMessage(WebSocketSession session) throws IOException {
        session.sendMessage(new TextMessage(getMonitoringMessage()));
    }

    private String getMonitoringMessage() throws JsonProcessingException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("local", repository.getLocalCenterValue());
        paramMap.put("process", repository.getAllProcessStatusValue());
        return om.writeValueAsString(paramMap);
    }
}
