package ru.kirillius.pf.sdn.core.Util;

import ru.kirillius.utils.logging.SystemLogger;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Resolves domain names to IPv4 addresses using the system {@code nslookup} command.
 */
public final class DomainUtil {
    private static final String CTX = DomainUtil.class.getSimpleName();

    private DomainUtil() {
    }

    /**
     * Returns a list of IPv4 addresses resolved for the provided domain.
     */
    public static List<String> lookup(String domain, String server) {
        if (domain == null || domain.isBlank()) {
            throw new IllegalArgumentException("Domain must not be null or blank");
        }

        var processBuilder = new ProcessBuilder("nslookup", domain.trim(), server.trim());
        processBuilder.redirectErrorStream(true);

        try {
            var process = processBuilder.start();
            if (!process.waitFor(10, TimeUnit.SECONDS)) {
                process.destroyForcibly();
                SystemLogger.error("nslookup timed out for domain " + domain, CTX);
                return List.of();
            }

            var output = readStream(process.getInputStream());
            if (process.exitValue() != 0) {
                SystemLogger.error("nslookup failed for domain " + domain + ": " + output, CTX);
                return List.of();
            }

            return extractIPv4(output);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            SystemLogger.error("nslookup was interrupted for domain " + domain + ": " + e.getMessage(), CTX);
        } catch (IOException e) {
            SystemLogger.error("Failed to execute nslookup for domain " + domain + ": " + e.getMessage(), CTX);
        } catch (Exception e) {
            SystemLogger.error("Unexpected error during nslookup for domain " + domain + ": " + e.getMessage(), CTX);
        }

        return List.of();
    }

    private static List<String> extractIPv4(String output) {
        var addresses = new LinkedHashSet<String>();
        var allowAddresses = false;

        for (var rawLine : output.split("\\R")) {
            var line = rawLine.trim();
            if (line.isEmpty()) {
                allowAddresses = false;
                continue;
            }

            if (line.startsWith("Name:")) {
                allowAddresses = true;
                continue;
            }

            if (!allowAddresses) {
                continue;
            }

            if (line.startsWith("Address:") || line.startsWith("Addresses:")) {
                var colonIndex = line.indexOf(':');
                if (colonIndex == -1 || colonIndex == line.length() - 1) {
                    continue;
                }

                var tokens = line.substring(colonIndex + 1).trim().split("\\s+");
                for (var token : tokens) {
                    try {
                        IPv4Util.validateAddress(token);
                        addresses.add(token);
                    } catch (IllegalArgumentException ignored) {
                        // skip invalid addresses such as IPv6
                    }
                }
            }
        }

        return new ArrayList<>(addresses);
    }

    private static String readStream(InputStream stream) throws IOException {
        try (stream) {
            return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
        }
    }
}
