package cz.senslog.connector.tools.senslog.auth;

import com.google.gson.reflect.TypeToken;
import cz.senslog.connector.tools.http.*;
import cz.senslog.connector.tools.util.Tuple;

import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;

public final class SensLogAuthService {

    private static final Map<String, SensLogAuthService> INSTANCES;

    private final Map<Tuple<String, String>, HttpCookie> authCookies;

    private final String sensLogUrl;

    private final URI baseUri;

    private final HttpClient httpClient;

    static {
        INSTANCES = new HashMap<>();
    }

    public static SensLogAuthService getInstance(String sensLogUrl) {
        if (INSTANCES.containsKey(sensLogUrl)) {
            return INSTANCES.get(sensLogUrl);
        } else {
            SensLogAuthService service = new SensLogAuthService(sensLogUrl);
            INSTANCES.put(sensLogUrl, service);
            return service;
        }
    }

    private SensLogAuthService(String sensLogUrl) {
        this.sensLogUrl = sensLogUrl;
        this.authCookies = new HashMap<>();
        this.httpClient = HttpClient.newHttpClient();
        try {
            this.baseUri = new URI(sensLogUrl);
        } catch (URISyntaxException e) {
            throw new RuntimeException("The config is in invalid format. " + e.getMessage());
        }
    }

    public synchronized HttpCookie getAuthCookie(String username, String password) {
        Tuple<String, String> credentials = Tuple.of(username, password);
        HttpCookie authCookie = authCookies.get(credentials);
        if (authCookie != null) {
            return authCookie;
        }

        HttpRequest request = HttpRequest.newBuilder().GET()
                .url(URLBuilder.newBuilder(sensLogUrl, "/ControllerServlet")
                        .addParam("username", username)
                        .addParam("password", password)
                        .build()
                ).build();

        HttpResponse response = httpClient.send(request);

        if (response.isError()) {
            return HttpCookie.empty();
        }

        final Type lastObsType = new TypeToken<Map<String, Object>>() {}.getType();
        Map<String, Object> jsonResponse = jsonToObject(response.getBody(), lastObsType);

        if (!jsonResponse.containsKey("sessionid")) {
            return HttpCookie.empty();
        }

        String sessionId = (String) jsonResponse.get("sessionid");
        String domain = baseUri.getHost();
        String path = baseUri.getPath();
        HttpCookie cookie = new HttpCookie("JSESSIONID", sessionId, domain, path);
        authCookies.put(credentials, cookie);
        return cookie;
    }
}
