package ctbrec.sites.jasmin;

import ctbrec.Config;
import ctbrec.sites.Site;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.Optional;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static ctbrec.io.HttpConstants.USER_AGENT;
import static java.nio.charset.StandardCharsets.UTF_8;

public class LiveJasminStreamStarter {

    private static final Logger LOG = LoggerFactory.getLogger(LiveJasminStreamStarter.class);
    private final CyclicBarrier barrier = new CyclicBarrier(2);

    void start(Site site, LiveJasminModelInfo modelInfo, LiveJasminStreamSource ss) {
        try {
            String websocketUrl = "wss://dss-live-{ipWithDashes}.dditscdn.com/h5live/http/playlist.m3u8?url={rtmpUrl}&stream={streamName}"
                    .replace("{ipWithDashes}", modelInfo.getSbIp().replace('.', '-'))
                    .replace("{rtmpUrl}", URLEncoder.encode(ss.rtmpUrl, UTF_8))
                    .replace("{streamName}", URLEncoder.encode(ss.streamName, UTF_8));

            Request webSocketRequest = new Request.Builder()
                    .url(websocketUrl)
                    .addHeader(USER_AGENT, Config.getInstance().getSettings().httpUserAgentMobile)
                    .build();
            LOG.debug("Websocket: {}", websocketUrl);
            site.getHttpClient().newWebSocket(webSocketRequest, new WebSocketListener() {
                @Override
                public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
                    LOG.debug("onOpen");
                }

                @Override
                public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {
                    String body = Optional.ofNullable(response).map(Response::body).map(responseBody -> {
                        try {
                            return responseBody.string();
                        } catch (IOException e) {
                            return "";
                        }
                    }).orElse("");
                    LOG.error("onFailure Body:[{}]", body, t);
                    awaitBarrier();
                    webSocket.close(1000, "");
                }

                @Override
                public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
                    LOG.debug("{}", new JSONObject(text).toString(2));
                    webSocket.close(1000, "");
                }

                @Override
                public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
                    LOG.trace("onMessageB");
                    super.onMessage(webSocket, bytes);
                }

                @Override
                public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
                    LOG.debug("onClosed {} {}", code, reason);
                    super.onClosed(webSocket, code, reason);
                }

                @Override
                public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
                    LOG.trace("onClosing {} {}", code, reason);
                    awaitBarrier();
                }
            });

            LOG.debug("Waiting for websocket to return");
            awaitBarrier();
            LOG.debug("Websocket is done.");
        } catch (Exception e) {
            LOG.error("Couldn't start stream", e);
        }
    }

    private void awaitBarrier() {
        try {
            barrier.await(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            LOG.error(e.getLocalizedMessage(), e);
        } catch (TimeoutException | BrokenBarrierException e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }
}
