package com.its.pis.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.its.app.AppUtils;
import com.its.app.utils.SysUtils;
import com.its.pis.entity.TbPisInfr;
import com.its.pis.entity.TbPisInfrStts;
import com.its.pis.global.AppRepository;
import com.its.pis.process.DbmsJobData;
import com.its.pis.process.DbmsJobProcess;
import com.its.pis.process.DbmsJobType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Slf4j
public class PisWebSocketHandler extends TextWebSocketHandler {

    protected String type;
    protected ObjectMapper mapper;
    protected DbmsJobProcess dbmsJobProcess;

    public PisWebSocketHandler(String type) {

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (Map.Entry<WebSocketSession, PisWebSocketSession> obj : PisWebSocketSessionManager.getInstance().getMap().entrySet()) {
                if (obj.getValue() != null) {
                }
            }
        }));

        this.type = type;
        this.mapper = new ObjectMapper();
        this.dbmsJobProcess = (DbmsJobProcess) AppUtils.getBean(DbmsJobProcess.class);

        log.info("PisWebSocketHandler() START, type={}", this.type);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 클라이언트가 연결되었을때 실행
        String ipAddr = session.getRemoteAddress().getHostString();
        String token = null;
        for (Map.Entry<String, List<String>> l : session.getHandshakeHeaders().entrySet()) {
            if ("sec-websocket-protocol".equals(l.getKey())) {
                token = l.getValue().get(0);
                break;
            }
        }
        if (token == null) {
            log.error("{}: afterConnectionEstablished: {}, token not found, session will be closed", this.type, ipAddr);
            session.close();
            return;
        }

        log.info("{}: afterConnectionEstablished: ipAddr={}, token={}", this.type, ipAddr, token);

        TbPisInfr pis = AppRepository.getInstance().getPisTokenMap().get(token);
        if (pis == null) {
            log.error("{}: afterConnectionEstablished: ipAddr={}, token={}, not found token, session will be closed", this.type, ipAddr, token);
            session.close();
            return;
        }

        if (pis.getSession() != null) {
            WebSocketSession pisSession = pis.getSession().getSession();
            if (pisSession != null && pisSession.isOpen()) {
                log.error("{}: afterConnectionEstablished: ipAddr={}, token={}, session already opened, old session will be closed", this.type, ipAddr, token);
                try {
                    pisSession.close();
                } catch(IOException e) {}
            }
        }

        PisWebSocketSession vo = new PisWebSocketSession(pis, session);
        pis.channelOpen(vo);
        PisWebSocketSessionManager.getInstance().addSession(session, vo);

        session.setBinaryMessageSizeLimit(1*1024*1024);
        session.setTextMessageSizeLimit(1*1024*1024);
        super.afterConnectionEstablished(session);

        log.info("{}: afterConnectionEstablished: connection accept, {}, {}", this.type, pis.getPIS_ID(), pis.getPIS_NM());

        // TODO: 주차정보시스템에 연결된 모든 주차장에 대해서 통신상태를 업데이트 해야함
        List<TbPisInfrStts> pisSttsList = Collections.synchronizedList(new ArrayList<>());
        String UPDT_DT = SysUtils.getSysTime();
        pis.getStts().setUPDT_DT(UPDT_DT);
        pisSttsList.add(pis.getStts());
        dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_PIS_STTS, false, pisSttsList, pis));
    }

    //클라이언트 연결을 끊었을 때 실행
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.warn("{}: afterConnectionClosed: session={}, URI={}, UUID={}", this.type, session.getRemoteAddress(), session.getUri(), session.getId());
        PisWebSocketSession sessionClient = PisWebSocketSessionManager.getInstance().getSession(session);
        if (sessionClient != null) {
            TbPisInfr pis = sessionClient.getObj();
            if (pis != null) {
                pis.channelClosed();

                // TODO: 주차정보시스템에 연결된 모든 주차장에 대해서 통신상태를 업데이트 해야함
                List<TbPisInfrStts> pisSttsList = Collections.synchronizedList(new ArrayList<>());
                String UPDT_DT = SysUtils.getSysTime();
                pis.getStts().setUPDT_DT(UPDT_DT);
                pisSttsList.add(pis.getStts());
                dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_PIS_STTS, false, pisSttsList, pis));
            }
        }
        else {
            log.warn("{}: afterConnectionClosed: Not Found session={}, URI={}, UUID={}", this.type, session.getRemoteAddress(), session.getUri(), session.getId());
        }

        PisWebSocketSessionManager.getInstance().removeSession(session);
        super.afterConnectionClosed(session, status);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("{}: handleTransportError: {}", this.type, exception.getMessage());
        if (session.isOpen()) {
            session.close();
        }
        super.handleTransportError(session, exception);
    }
}
