package ru.kirillius.pf.sdn.External.API;

import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import org.json.JSONTokener;
import ru.kirillius.pf.sdn.core.Networking.ASInfoProvider;
import ru.kirillius.pf.sdn.core.Networking.IPv4Subnet;
import ru.kirillius.utils.logging.SystemLogger;

import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Retrieves ASN prefix information from RIPE public API.
 */
public class RIPEInfoProvider implements ASInfoProvider {

    /**
     * Fetches IPv4 prefixes announced by the specified autonomous system.
     */
    @Override
    @SneakyThrows
    public List<IPv4Subnet> getPrefixes(int as) {
        try (var client = HttpClient.newHttpClient()) {
            var request = HttpRequest.newBuilder()
                    .uri(URI.create("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + as))
                    .header("Accept", "application/json").timeout(Duration.ofMinutes(2)).GET().build();
            var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
            if (response.statusCode() == 200) {
                try (var inputStream = response.body()) {
                    return getIPv4Subnets(inputStream);
                }
            } else {
                throw new RuntimeException("Unable to get info about AS" + as);
            }
        }
    }

    @Override
    public IPQueryInfo queryAddress(String address) {
        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://stat.ripe.net/data/network-info/data.json?resource=" + address))
                .header("Accept", "application/json")
                .GET()
                .build();

        try (var client = HttpClient.newHttpClient()) {
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() != 200) {
                SystemLogger.error("Unable to get info about IP " + address + ", status " + response.statusCode(), CTX);
                return emptyIPInfo();
            }
            var json = new JSONObject(new JSONTokener(response.body()));
            var data = json.getJSONObject("data");
            var asns = data.getJSONArray("asns");
            var prefix = data.getString("prefix");

            var asnList = new ArrayList<Integer>();
            asns.forEach(obj -> asnList.add(Integer.parseInt(obj.toString())));

            return IPQueryInfo.builder().prefixes(Collections.singletonList(new IPv4Subnet(prefix))).ASN(asnList).build();
        } catch (Exception e) {
            SystemLogger.error("Failed to query info about IP " + address + ": " + e.getMessage(), CTX);
            return emptyIPInfo();
        }
    }

    /**
     * Parses IPv4 prefix entries from the Hurricane Electric API response.
     */
    private static @NotNull ArrayList<IPv4Subnet> getIPv4Subnets(InputStream inputStream) {
        var json = new JSONObject(new JSONTokener(inputStream));
        var data = json.getJSONObject("data");
        var array = data.getJSONArray("prefixes");
        var list = new ArrayList<IPv4Subnet>();
        array.forEach(obj -> {
            var jo = (JSONObject) obj;

            var prefix = jo.getString("prefix");
            if (prefix.indexOf('.') == -1) {
                return;
            }
            list.add(new IPv4Subnet(prefix));

        });
        return list;
    }

    private final static String CTX = RIPEInfoProvider.class.getSimpleName();

    private static IPQueryInfo emptyIPInfo() {
        return IPQueryInfo.builder()
                .ASN(Collections.emptyList())
                .prefixes(Collections.emptyList())
                .build();
    }

}
