package com.its.pis.websocket;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.its.pis.websocket.C2F.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Map;

@Slf4j
@Controller
@RequestMapping(ItsWebSocketConfig.WS_ENDPOINT)
public class ItsWebSocketHandler extends TextWebSocketHandler {

    private ObjectMapper mapper;

    public ItsWebSocketHandler() {

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (Map.Entry<org.springframework.web.socket.WebSocketSession, ItsWebSocketSession> obj : ItsWebSocketSessionManager.getInstance().getMap().entrySet()) {
                if (obj.getValue() != null) {
                }
            }
        }));

        this.mapper = new ObjectMapper();

        log.info("ItsWebSocketHandler() START");
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 클라이언트가 연결되었을때 실행
        log.info("afterConnectionEstablished: {}, {}, {}, {}", session.getBinaryMessageSizeLimit(), session.getTextMessageSizeLimit(), session.getRemoteAddress().getHostString(), session.getAcceptedProtocol());
        log.info("afterConnectionEstablished: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
        session.setBinaryMessageSizeLimit(1*1024*1024);
        session.setTextMessageSizeLimit(1*1024*1024);
        super.afterConnectionEstablished(session);

        ItsWebSocketSession vo = new ItsWebSocketSession(this, session);
        ItsWebSocketSessionManager.getInstance().addSession(session, vo);
    }

    //클라이언트가 웹소켓 서버로 메시지를 전송했을 때 실행
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("handleTextMessage: {}, {}, {}, {}", session.getBinaryMessageSizeLimit(), session.getTextMessageSizeLimit(), session.getRemoteAddress().getHostString(), session.getAcceptedProtocol());
        log.info("handleTextMessage: " + session.getRemoteAddress() + ",  Uri[" + session.getUri() + "], UUID[" + session.getId() + "]");//, message[" + message.getPayload() + "]");
        /*List<String> requests = ItsUtils.split(message.getPayload(), ":");
        if (requests.size() != 2) {
            log.error("Request data parsing error: {}", requests);
            session.close();
            return;
        }
*/

        //C2FMessage<C2FMessagePayload> c2fMessage = this.mapper.readValue(message.getPayload(), C2FMessage.class);
        C2FMessage<C2FMessagePayload> c2fMessage = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<C2FMessagePayload>>(){});
        log.error("{}", c2fMessage);

        try {
            String eventName = c2fMessage.getData().getPayload().getEvent_name();
            if ("prk_plce_sttus_info".equals(eventName)) {
                C2FMessage<PrkPlceSttusInfo> sttusInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceSttusInfo>>(){});
                log.error("{}", this.mapper.writeValueAsString(sttusInfo));
            } else if ("prk_plce_opr_info".equals(eventName)) {
                C2FMessage<PrkPlceOprInfo> oprInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceOprInfo>>(){});
                //PrkPlceOprInfo oprInfo = (PrkPlceOprInfo)c2fMessage.getData().getPayload();
                log.error("{}", this.mapper.writeValueAsString(oprInfo));
            } else if ("prk_plce_rl_time_info".equals(eventName)) {
                C2FMessage<PrkPlceRlTimeResponseInfo> rlTimeInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceRlTimeResponseInfo>>(){});
                //PrkPlceRlTimeInfo rlTimeInfo = (PrkPlceRlTimeInfo)c2fMessage.getData().getPayload();
                log.error("{}", this.mapper.writeValueAsString(rlTimeInfo));
            } else if ("prk_plce_reservation_response_info".equals(eventName)) {
                C2FMessage<PrkPlceReservationResponseInfo> reservationInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceReservationResponseInfo>>(){});
                //PrkPlceReservationResponseInfo reservationInfo = (PrkPlceReservationResponseInfo)c2fMessage.getData().getPayload();
                log.error("{}", this.mapper.writeValueAsString(reservationInfo));
            } else if ("prk_plce_vhcl_location_response_info".equals(eventName)) {
                C2FMessage<PrkPlceVhclLocationResponseInfo> locationInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceVhclLocationResponseInfo>>(){});
                //PrkPlceVhclLocationResponseInfo locationInfo = (PrkPlceVhclLocationResponseInfo)c2fMessage.getData().getPayload();
                log.error("{}", this.mapper.writeValueAsString(locationInfo));
            } else {
                log.error("Unknown event name: {}", eventName);
                log.error("Payload: {}", message.getPayload());
            }
        } catch(NullPointerException e) {
            log.error("Payload data null. {}", message.getPayload());
        }

        ItsWebSocketSession sessionClient = ItsWebSocketSessionManager.getInstance().getSession(session);
        if (sessionClient == null) {
            log.error("Request session not found: {}", session);
            session.close();
            return;
        }
        //log.info("Payload: {}", message.getPayload());
    }

    //클라이언트 연결을 끊었을 때 실행
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.info("afterConnectionClosed: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
        ItsWebSocketSessionManager.getInstance().removeSession(session);
        super.afterConnectionClosed(session, status);
    }

}
