package ru.dragonestia.kubik.client.server;

import com.google.gson.Gson;
import lombok.Getter;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.RequestBody;
import ru.dragonestia.kubik.client.Kubik;
import ru.dragonestia.kubik.dto.ServerRegistrationData;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class ServerControl {

    private static final Gson gson = new Gson();

    private final Kubik kubik;
    private final ScheduledExecutorService executor;
    @Getter private final String groupId;
    @Getter private final int port;
    @Getter private String id;
    private ScheduledFuture<?> future;

    private final Object lock = new Object();

    public ServerControl(Kubik kubik, ScheduledExecutorService executor, String groupId, int port) {
        this.kubik = kubik;
        this.executor = executor;
        this.groupId = groupId;
        this.port = port;

        executor.execute(this::doRegisterServer);
    }

    private void doRegisterServer() {
        synchronized (lock) {
            try {
                registerServer();
                future = executor.schedule(this::doRegisterServer, kubik.getConfig().getHeartbeatDuration(), TimeUnit.SECONDS);
            } catch (Exception ex) {
                future = executor.schedule(this::doRegisterServer, kubik.getConfig().getReconnectDelay(), TimeUnit.SECONDS);
            }
        }
    }

    private void registerServer() {
        var request = new Request.Builder()
                .url(HttpUrl.get(kubik.getHttpUrl() + "/api/v1/server").newBuilder()
                        .addQueryParameter("groupId", groupId)
                        .addQueryParameter("port", Integer.toString(port))
                        .build())
                .post(RequestBody.EMPTY)
                .build();

        try (var response = kubik.getOkHttpClient().newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("Taken bad response: %s, %s".formatted(response.code(), response.message()));
            }

            var responseObject = gson.fromJson(response.body().string(), ServerRegistrationData.class);
            id = responseObject.id();
        } catch (Exception ex) {
            id = null;
            throw new RuntimeException(ex);
        }
    }

    public void dispose() {
        synchronized (lock) {
            if (id == null) return;
            if (future != null) {
                future.cancel(false);
                future = null;
            }
            unregisterServer();
        }
    }

    private void unregisterServer() {
        var request = new Request.Builder()
                .url(HttpUrl.get(kubik.getHttpUrl() + "/api/v1/server").newBuilder()
                        .addQueryParameter("id", id)
                        .build())
                .delete()
                .build();

        try (var response = kubik.getOkHttpClient().newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("Taken bad response: %s, %s".formatted(response.code(), response.message()));
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } finally {
            id = null;
        }
    }
}
