package me.proxylink.plugin.tls;

import me.proxylink.common.AgentConfig;
import me.proxylink.common.AgentConfigIO;
import me.proxylink.plugin.ConfigFileEditor;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.logging.Logger;

public final class TlsBootstrap {
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
    private static final HexFormat HEX = HexFormat.of().withLowerCase();

    private TlsBootstrap() {
    }

    public static AgentConfig prepare(Path dataFolder, Path configPath, AgentConfig config, Logger logger)
            throws IOException, GeneralSecurityException {
        if (!config.tls().enabled()) {
            return config;
        }

        if (config.role() == AgentConfig.Role.FRONTEND && config.tls().autoGenerate()) {
            return prepareFrontend(dataFolder, configPath, config, logger);
        }

        if (config.role() == AgentConfig.Role.BACKEND
                && config.tls().trustOnFirstUse()
                && config.tls().pinnedCertificateSha256().isBlank()) {
            logger.warning("TLS trust-on-first-use is enabled. The first successful frontend certificate will be pinned in config.properties.");
        }

        return config;
    }

    private static AgentConfig prepareFrontend(Path dataFolder, Path configPath, AgentConfig config, Logger logger)
            throws IOException, GeneralSecurityException {
        Path keyStorePath = config.tls().keyStorePath();
        if (keyStorePath == null) {
            keyStorePath = dataFolder.resolve("certs/frontend.p12").normalize();
            ConfigFileEditor.setProperty(configPath, "tunnel.tls.keyStore", "certs/frontend.p12");
        }

        String password = config.tls().keyStorePassword();
        if (password == null || password.isBlank()) {
            password = randomHex(32);
            ConfigFileEditor.setProperty(configPath, "tunnel.tls.keyStorePassword", password);
        }

        String fingerprint;
        if (Files.exists(keyStorePath)) {
            fingerprint = SelfSignedCertificateGenerator.fingerprintFromPkcs12(keyStorePath, password.toCharArray());
            logger.info("Using existing DirtSimpleP2P TLS certificate: " + fingerprint);
        } else {
            fingerprint = SelfSignedCertificateGenerator.createPkcs12(keyStorePath, password.toCharArray(), config.nodeName());
            logger.info("Generated DirtSimpleP2P TLS certificate: " + fingerprint);
        }

        logger.info("Backend nodes should pin this TLS fingerprint automatically on first connect, or manually set tunnel.tls.pinnedCertificateSha256.");
        return AgentConfigIO.load(configPath);
    }

    private static String randomHex(int bytes) {
        byte[] data = new byte[bytes];
        SECURE_RANDOM.nextBytes(data);
        return HEX.formatHex(data);
    }
}
