package com.its.pis.websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.its.pis.entity.TbPisInfr;
import com.its.pis.websocket.common.SubscribeIdentifier;
import com.its.pis.websocket.common.SubscribeRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

@Slf4j
public class PisWebSocketHandlerC2C_C extends PisWebSocketHandlerC2C {
    private final TbPisInfr pis;
    private String wsUrl;
    private WebSocketHttpHeaders httpHeaders;
    private SockJsClient sockJsClient;

    public PisWebSocketHandlerC2C_C(TbPisInfr pis) {
        super("C2C-C");
        log.info("PisWebSocketHandlerC2C_C() START");
        this.pis = pis;
        this.httpHeaders = new WebSocketHttpHeaders();
        this.httpHeaders.setSecWebSocketProtocol(this.pis.getPIS_TOKEN());

        List<Transport> transports = new ArrayList<>(2);
        transports.add(new WebSocketTransport(new StandardWebSocketClient()));
        transports.add(new RestTemplateXhrTransport());
        this.sockJsClient = new SockJsClient(transports);

        if (this.pis.getPIS_END_POINT().contains("ws")) {
            this.wsUrl = this.pis.getPIS_END_POINT();
        } else {
            if (this.pis.getPIS_PORT() == 0 || this.pis.getPIS_PORT() == 80) {
                this.wsUrl = "ws://" + this.pis.getPIS_IP() + this.pis.getPIS_END_POINT();
            } else {
                this.wsUrl = "ws://" + this.pis.getPIS_IP() + ":" + this.pis.getPIS_PORT() + this.pis.getPIS_END_POINT();
            }
        }
    }

    /**
     * C2C-Server 로 웹소켓 연결을 시도한다.
     */
    public void connect() {
        try {
            URI url = new URI(this.wsUrl);
            this.sockJsClient.doHandshake(this, this.httpHeaders, url).addCallback(
                new ListenableFutureCallback<WebSocketSession>() {
                    @Override
                    public void onSuccess(WebSocketSession result) {
                    }
                    @Override
                    public void onFailure(Throwable ex) {
                        log.error("{}: connect: onFailure, {}", type, ex.getMessage());
                        reConnect();
                    }
                }
            );
        } catch (URISyntaxException e) {
            log.error("{}: connect: Exception, {}", this.type, e.getMessage());
        }
    }

    public void reConnect() {
        Timer timer = new Timer(true);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                connect();
            }
        };
        timer.schedule(timerTask, 5000);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 서버로 연결이 성공 하였을 때 실행됨
        String ipAddr = session.getRemoteAddress().getHostString();
        log.info("{}: afterConnectionEstablished: ipAddr={}", this.type, ipAddr);

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

        session.setBinaryMessageSizeLimit(1*1024*1024);
        session.setTextMessageSizeLimit(1*1024*1024);
        log.info("{}: afterConnectionEstablished: connection accept, {}, {}", this.type, this.pis.getPIS_ID(), this.pis.getPIS_NM());

        updatePisStts(pis, true);

        // Subscribe 등록 요청
        SubscribeRequest subscribe = SubscribeRequest.builder()
                .command("subscribe")
                .identifier(SubscribeIdentifier.builder()
                        .channel("ParkingLotChannel").build())
                .build();
        try {
            String strMessage = this.mapper.writeValueAsString(subscribe);
            this.pis.getSession().sendMessage(subscribe.getCommand(), new TextMessage(strMessage));
        } catch (JsonProcessingException e) {
            log.error("TX] Exception: {}", e.getMessage());
        }
    }
    //클라이언트 연결을 끊었을 때 실행
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        reConnect();
    }

}
