package com.r35157.service.notification.impl.pushover;

import com.r35157.service.notification.BoundNotificationService;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

/**
 * Sends bound notification messages through the Pushover Messages API.
 */
public class PushoverNotificationService implements BoundNotificationService {

    /**
     * Creates a Pushover service bound to an application token and user or
     * group key.
     *
     * @param token Pushover application token
     * @param userName Pushover user or group key
     */
    public PushoverNotificationService(ΩAPIKeyΩ token, ΩUserNameΩ userName) {
        this.httpClient = HttpClient.newHttpClient();
        this.token = token;
        this.userName = userName;
    }

    @Override
    public void push(@NotNull ΩNotificationMessageΩ message) throws IOException {
        validatePushoverMessage(message);

        String body = formEncode(
                "token", token,
                "user", userName,
                "message", message
        );

        HttpRequest request = HttpRequest.newBuilder()
                .uri(PUSHOVER_MESSAGES_URI)
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> response;

        try {
            response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        } catch(InterruptedException ie) {
            throw new IOException("Pushover request was interrupted!", ie);
        }

        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IOException("Pushover request failed! HTTP "
                    + response.statusCode() + ": " + response.body());
        }
    }

    private static void validatePushoverMessage(ΩNotificationMessageΩ message) {
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("Notification message cannot be blank");
        }
    }

    private static String formEncode(String... keyValues) {
        if (keyValues.length % 2 != 0) {
            throw new IllegalArgumentException("keyValues must contain key/value pairs");
        }

        StringBuilder result = new StringBuilder();

        for (int i = 0; i < keyValues.length; i += 2) {
            if (!result.isEmpty()) {
                result.append('&');
            }

            result.append(encode(keyValues[i]))
                    .append('=')
                    .append(encode(keyValues[i + 1]));
        }

        return result.toString();
    }

    private static String encode(String value) {
        return URLEncoder.encode(value, StandardCharsets.UTF_8);
    }

    private void test() throws IOException {
        ΩAPIKeyΩ apiKey = "SomeKey";
        ΩUserNameΩ userName = "userNmae";

        BoundNotificationService notificationService =
                new PushoverNotificationService(apiKey, userName);

        ΩNotificationMessageΩ message = "Hello World!";
        notificationService.push(message);
    }

    private static final URI PUSHOVER_MESSAGES_URI =
            URI.create("https://api.pushover.net/1/messages.json");

    private final HttpClient httpClient;
    private final ΩAPIKeyΩ token;
    private final ΩUserNameΩ userName;
}
