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

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
import ru.kirillius.json.JSONUtility;

import java.io.*;
import java.net.ConnectException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

/**
 * CLI utility that requests managed routes from the SDN API and writes them into an OpenVPN push file.
 */
public class App {
    /**
     * Loads connector configuration from {@code ovpn-connector.json}.
     */
    private static Config loadConfig() {
        var configFile = new File("ovpn-connector.json");
        if (!configFile.exists()) {
            throw new RuntimeException("Config file " + configFile.getAbsolutePath() + " does not exist");
        }
        try (var stream = new FileInputStream(configFile)) {
            return JSONUtility.deserializeStructure(new JSONObject(new JSONTokener(stream)), Config.class);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load config file", e);
        }
    }

    /**
     * Writes the received routes to the OpenVPN push file.
     */
    private static void pushRoutes(JSONArray routes, File pushFile) {
        try (var writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pushFile)))) {
            for (var i = 0; i < routes.length(); i++) {
                var entry = routes.getJSONObject(i);
                var route = "push \"route " + entry.getString("address") + " " + entry.getString("mask") + "\"\n";
                writer.write(route);
            }
            writer.flush();
        } catch (IOException ex) {
            throw new RuntimeException("Failed to write PUSH file", ex);
        }
    }

    /**
     * Application entry point.
     */
    public static void main(String[] args) {
        try {
            var config = loadConfig();

            if (args.length < 1) {
                throw new RuntimeException("OVPN Push file is not specified in ARGS");
            }
            var pushFile = new File(args[0]);
            var routes = sendRequest(config);
            pushRoutes(routes, pushFile);
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace(System.err);
            System.exit(1);
        }
    }

    /**
     * Performs the JSON-RPC request to the SDN API and returns the route list.
     */
    private static JSONArray sendRequest(Config config) throws IOException, InterruptedException {
        var json = new JSONObject();
        json.put("jsonrpc", "2.0");
        json.put("method", "ru.kirillius.pf.sdn.External.API.Components.OVPN::getManagedRoutes");
        json.put("params", new JSONObject());
        json.put("id", 1);


        try (var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build()) {
            // Создаем HTTP запрос
            var request = HttpRequest.newBuilder()
                    .uri(URI.create(config.getHost() + "/jsonrpc/rpc"))
                    .header("Content-Type", "application/json")
                    .header("X-Auth-token", config.getToken())
                    .POST(HttpRequest.BodyPublishers.ofString(json.toString()))
                    .timeout(Duration.ofSeconds(30))
                    .build();

            // Отправляем запрос и получаем ответ
            var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            // Проверяем статус ответа
            if (response.statusCode() != 200) {
                throw new RuntimeException("HTTP error code: " + response.statusCode() + " body: " + response.body());
            }

            return new JSONArray(new JSONObject(response.body()).getJSONArray("result"));
        } catch (ConnectException e) {
            throw new RuntimeException("Unable to connect to remote server", e);
        }
    }
}
