package com.atlassian.bitbucket.internal.ssh.server;

import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.google.common.collect.Iterables;
import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;

import javax.annotation.Nonnull;
import java.io.*;
import java.security.KeyPair;

import static com.atlassian.bitbucket.ssh.util.KeyUtils.calculateFingerprint;

/**
 * Provide the public/private keys of the server, to allow it to serve SSH requests.
 * <p>
 * Note: The server <i>only</i> supports auto-generating a key. It does not allow its key to be configured or edited
 * directly, even by administrators.
 */
public class DefaultHostKeyPairProvider extends AbstractGeneratorHostKeyProvider implements HostKeyPairProvider {

    /**
     * Defines the name of the file, stored in the config directory, where the SSH server will persist its
     * key between runs.
     */
    private static final String KEY_PAIR_FILE = "ssh-server-keys.pem";

    /** The fingerprint of the public key as a string of hex digits **/
    private String fingerprint;

    /**
     * Sets the path to the {@link #KEY_PAIR_FILE key pair file}, since SpEL can't be used in Spring 2.5 (which is the
     * version available inside the plugin framework).
     *
     * @param service the application properties service, used to obtain the config directory
     */
    public DefaultHostKeyPairProvider(ApplicationPropertiesService service) {
        setFile(new File(service.getConfigDir(), KEY_PAIR_FILE));
    }

    /**
     * Returns the fingerprint of the public key of the server as a string of hex digits.
     */
    @Nonnull
    @Override
    public String getFingerprint() {
        //Calculated lazily to avoid loading the PEM if SSH is never used
        if (fingerprint == null) {
            //loadKeys() is synchronized so at worst we get
            //multiple fingerprint calculations and assignments of the same value
            KeyPair keyPair = Iterables.get(loadKeys(), 0);
            fingerprint = calculateFingerprint(keyPair.getPublic());
        }
        return fingerprint;
    }

    @Override
    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException {
        PEMParser parser = new PEMParser(new InputStreamReader(inputStream));

        Object parsed = parser.readObject();
        if (parsed instanceof PEMKeyPair) {
            JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
            pemConverter.setProvider("BC");

            return pemConverter.getKeyPair((PEMKeyPair) parsed);
        } else if (parsed instanceof KeyPair) {
            return (KeyPair) parsed;
        }

        return null;
    }

    @Override
    protected void doWriteKeyPair(String resourceKey, KeyPair keyPair, OutputStream outputStream) throws IOException {
        JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(outputStream));
        writer.writeObject(keyPair);
        writer.flush();
    }
}
