package lc.mecha.http.server;

import lc.mecha.http.client.HttpClientRig;
import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import lc.mecha.security.CertUtil;
import lc.mecha.security.CertificateKeyStore;
import lc.mecha.security.KeyUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.operator.OperatorCreationException;
import org.junit.Test;

import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import static org.junit.Assert.assertEquals;

/**
 * @author Alex Leigh
 * @since mk20 (TOKYO PROTOCOL)
 */
public class HttpServerTest {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(HttpServerTest.class);


    @Test
    public void testPlainListener() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        HttpServer ws = new HttpServer();
        ws.addListener(new ListenerConfig(9500));
        ws.start();

        HttpClientRig rig = new HttpClientRig(true, null);
        CloseableHttpClient client = rig.getClient();
        HttpGet httpGet = new HttpGet("http://localhost:9500");

        try (CloseableHttpResponse response1 = client.execute(httpGet)) {
            assertEquals("HTTP/1.1 404 Not Found", response1.getStatusLine().toString());
            System.out.println(response1.getStatusLine());
            HttpEntity entity1 = response1.getEntity();
            EntityUtils.consume(entity1);
        } catch (ClientProtocolException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void testSslListener() throws KeyStoreException, CertificateException, IOException,
            NoSuchAlgorithmException, KeyManagementException, OperatorCreationException {
        final var ws = new HttpServer();
        final var ks = KeyStore.getInstance(KeyStore.getDefaultType());
        final var pw = "abc123";

        ks.load(null, null);

        var kp = KeyUtil.generateRSAKeyPair(1024);
        var cert = CertUtil.selfSign(kp, "cn=localhost");
        var keyPass = pw.toCharArray();
        var chain = new X509Certificate[]{cert};
        var cks = new CertificateKeyStore(ks, pw);

        // Note that keypass is mandatory
        ks.setKeyEntry("jetty", kp.getPrivate(), keyPass, chain);

        logger.info("Generated key & cert. [key: {}] [cert: {}]", kp, cert);

        ws.addListener(new ListenerConfig(9502, cks));
        ws.start();

        HttpClientRig rig = new HttpClientRig(false, cks);
        CloseableHttpClient client = rig.getClient();
        HttpGet httpGet = new HttpGet("https://localhost:9502");

        try (CloseableHttpResponse response1 = client.execute(httpGet)) {
            assertEquals("HTTP/1.1 404 Not Found", response1.getStatusLine().toString());
            System.out.println(response1.getStatusLine());
            HttpEntity entity1 = response1.getEntity();
            EntityUtils.consume(entity1);
        }
    }


    @Test
    public void testBadSslCert() throws Exception {
        final var ws = new HttpServer();
        final var ks = KeyStore.getInstance(KeyStore.getDefaultType());
        final var pw = "abc123";

        ks.load(null, null);

        var kp = KeyUtil.generateRSAKeyPair(1024);
        var cert = CertUtil.selfSign(kp, "cn=BADBADBAD");
        var keyPass = pw.toCharArray();
        var chain = new X509Certificate[]{cert};
        var cks = new CertificateKeyStore(ks, pw);

        // Note that keypass is mandatory
        ks.setKeyEntry("jetty", kp.getPrivate(), keyPass, chain);

        logger.info("Generated key & cert. [key: {}] [cert: {}]", kp, cert);

        ws.addListener(new ListenerConfig(9501, cks));
        ws.start();

        HttpClientRig rig = new HttpClientRig(false, cks);
        CloseableHttpClient client = rig.getClient();
        HttpGet httpGet = new HttpGet("https://localhost:9501");

        try (CloseableHttpResponse response1 = client.execute(httpGet)) {
            assertEquals("HTTP/1.1 404 Not Found", response1.getStatusLine().toString());
            System.out.println(response1.getStatusLine());
            HttpEntity entity1 = response1.getEntity();
            EntityUtils.consume(entity1);
        } catch (SSLPeerUnverifiedException e) {
            // This means it worked
            return;
        }

        // If we got this far, it failed to detect the bad certificate
        throw new Exception("Test failed");
    }
}
