package com.codebyte.api.config.ssl;

import static org.assertj.core.api.Assertions.assertThat;

import com.codebyte.api.support.AbstractPostgresIT;
import com.codebyte.api.support.TestPki;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

/**
 * Boots the app with TLS enabled from the generated dev trio and performs a real HTTPS request with
 * a client that trusts only the dev root. It asserts the server presented <strong>leaf +
 * intermediate</strong> — the proof the two-file (cert + CA) problem is actually solved.
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class HttpsChainIT extends AbstractPostgresIT {

    private static final TestPki PKI = TestPki.threeLevel();
    private static final Path SSL_DIR;

    static {
        try {
            SSL_DIR = Files.createTempDirectory("https-chain-it");
            Files.writeString(SSL_DIR.resolve("tls.crt"), PKI.leafCertPem());
            Files.writeString(SSL_DIR.resolve("ca.crt"), PKI.caChainPem());
            Files.writeString(SSL_DIR.resolve("tls.key"), PKI.keyPkcs8Pem());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @LocalServerPort private int port;

    @DynamicPropertySource
    static void sslProperties(DynamicPropertyRegistry registry) {
        registry.add("server.ssl.enabled", () -> "true");
        registry.add("server.ssl.bundle", () -> "server");
        registry.add("app.ssl.enabled", () -> "true");
        registry.add("app.ssl.certificate", () -> SSL_DIR.resolve("tls.crt").toString());
        registry.add("app.ssl.ca", () -> SSL_DIR.resolve("ca.crt").toString());
        registry.add("app.ssl.private-key", () -> SSL_DIR.resolve("tls.key").toString());
    }

    @Test
    void serves_the_full_chain_over_https() throws Exception {
        // Client trusts ONLY the dev root, so a successful handshake also proves the chain
        // validates.
        KeyStore trust = KeyStore.getInstance(KeyStore.getDefaultType());
        trust.load(null, null);
        trust.setCertificateEntry("root", PKI.root);
        TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trust);
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(null, tmf.getTrustManagers(), null);

        URI uri = URI.create("https://localhost:" + port + "/actuator/health");
        HttpsURLConnection connection = (HttpsURLConnection) uri.toURL().openConnection();
        connection.setSSLSocketFactory(ctx.getSocketFactory());
        connection.connect();

        Certificate[] served = connection.getServerCertificates();
        assertThat(connection.getResponseCode()).isEqualTo(200);
        // Leaf first, then the intermediate — the root is not served.
        assertThat(served).hasSize(2);
        assertThat(((X509Certificate) served[0]).getSubjectX500Principal().getName())
                .contains("localhost");
        assertThat(((X509Certificate) served[1]).getSubjectX500Principal().getName())
                .contains("Intermediate");
        connection.disconnect();
    }
}
