import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class GetToken {
    public static void main(String[] args) throws IOException {
        URI baseUri = URI.create("http://localhost:59733");
        String clientId = "get-token";
        String clientSecret = "get-token-secret";

        System.out.println("Browse to " + baseUri.resolve("authorize?response_type=code&client_id=" + clientId));

        HttpClient httpClient = HttpClient.newBuilder()
                .authenticator(new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(clientId, clientSecret.toCharArray());
                    }
                })
                .build();

        HttpServer httpServer = HttpServer.create();
        httpServer.bind(new InetSocketAddress(59732), 0);

        Thread thread = Thread.currentThread();

        httpServer.createContext("/", exchange -> {
            exchange.sendResponseHeaders(200, 0);
            try (OutputStream responseBody = exchange.getResponseBody()) {
                responseBody.write("All done, close tab".getBytes(StandardCharsets.UTF_8));
            }

            Map<String, List<String>> queryParams = getQueryParams(exchange);
            String code = queryParams.get("code").get(0);
            HttpRequest httpRequest = HttpRequest.newBuilder()
                    .uri(baseUri.resolve("exchange"))
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .POST(HttpRequest.BodyPublishers.ofString("grant_type=authorization_code&code=" + code))
                    .build();
            try {
                HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
                System.out.println(response.body());

                // Try to copy the access token to the clipboard
                Matcher matcher = Pattern.compile("access_token\":\"([^\"]+)\"")
                        .matcher(response.body());
                if (matcher.find()) {
                    StringSelection clipboardData = new StringSelection(matcher.group(1));
                    Toolkit.getDefaultToolkit()
                            .getSystemClipboard()
                            .setContents(clipboardData, clipboardData);
                    try { Thread.sleep(1_000L); } catch (InterruptedException e) { }
                    System.out.println("Access token copied to clipboard (probably)");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                thread.interrupt();
            }
        });

        httpServer.start();
        try {
            Thread.sleep(Duration.ofMinutes(1L).toMillis());
            System.out.println("No authorization within one minute, exiting.");
            System.exit(1);
        } catch (InterruptedException ignored) {
            // expected
        }
        httpServer.stop(0);
    }

    private static Map<String, List<String>> getQueryParams(final HttpExchange exchange) {
        String query = exchange.getRequestURI()
                .getQuery();
        return Arrays.stream(query.split("&"))
                .map(s -> s.split("="))
                .collect(Collectors.groupingBy(
                        split -> split[0],
                        Collectors.mapping(split -> split[1], Collectors.toList())));
    }
}