package de.cronn.proxy.ssh;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.JSch;

import de.cronn.proxy.ssh.util.Utils;

public final class JSchHelper {

	private static final Logger log = LoggerFactory.getLogger(JSchHelper.class);

	private static final String SERVER_HOST_KEY_SEPARATOR = ",";

	private static final String JSCH_CONFIG_KEY_SERVER_HOST_KEY = "server_host_key";
	private static final String JSCH_CONFIG_KEY_PREFERRED_AUTHENTICATIONS = "PreferredAuthentications";

	private static class HostKeyComparator implements Comparator<HostKeyType> {

		private final List<HostKeyType> sortOrder;

		protected HostKeyComparator(List<HostKeyType> sortOrder) {
			this.sortOrder = sortOrder;
		}

		protected HostKeyComparator(HostKeyType... sortOrder) {
			this(Arrays.asList(sortOrder));
		}

		@Override
		public int compare(HostKeyType a, HostKeyType b) {
			int indexA = sortOrder.indexOf(a);
			int indexB = sortOrder.indexOf(b);
			return Integer.compare(indexA, indexB);
		}
	}

	private static final Comparator<HostKeyType> CMP_PREFER_ECDSA = new HostKeyComparator(
		HostKeyType.ECDSA256, HostKeyType.ECDSA384, HostKeyType.ECDSA521, HostKeyType.SSH_RSA, HostKeyType.SSH_DSS
	);

	private static final Comparator<HostKeyType> CMP_PREFER_RSA = new HostKeyComparator(
		HostKeyType.SSH_RSA, HostKeyType.ECDSA256, HostKeyType.ECDSA384, HostKeyType.ECDSA521, HostKeyType.SSH_DSS
	);

	public enum ServerHostKeySortOrder {
		PREFER_ECDSA,
		PREFER_RSA,
	}

	private JSchHelper() {
	}

	protected static void reconfigureServerHostKeyOrder(ServerHostKeySortOrder hostKeySortOrder) {
		List<HostKeyType> serverHostKeys = new ArrayList<>(getServerHostKeys());
		if (hostKeySortOrder == ServerHostKeySortOrder.PREFER_ECDSA) {
			serverHostKeys.sort(CMP_PREFER_ECDSA);
		} else if (hostKeySortOrder == ServerHostKeySortOrder.PREFER_RSA) {
			serverHostKeys.sort(CMP_PREFER_RSA);
		} else {
			throw new IllegalArgumentException("Unknown host key sort order: " + hostKeySortOrder);
		}

		if (!getServerHostKeys().equals(serverHostKeys)) {
			log.debug("changing server host key order to: {}", serverHostKeys);

			List<String> serverHostKeyNames = new ArrayList<>();
			for (HostKeyType serverHostKey : serverHostKeys) {
				serverHostKeyNames.add(serverHostKey.getTypeString());
			}

			String newHostKeyOrder = Utils.join(serverHostKeyNames, SERVER_HOST_KEY_SEPARATOR);
			JSch.setConfig(JSCH_CONFIG_KEY_SERVER_HOST_KEY, newHostKeyOrder);
		}
	}

	protected static void reconfigurePreferredAuthentications() {
		JSch.setConfig(JSCH_CONFIG_KEY_PREFERRED_AUTHENTICATIONS, "publickey");
	}

	protected static void registerLogger() {
		JSch.setLogger(new JSchSlf4JLogger());
	}

	protected static List<HostKeyType> getServerHostKeys() {
		String serverHostKey = JSch.getConfig(JSCH_CONFIG_KEY_SERVER_HOST_KEY);

		List<HostKeyType> hostKeyTypes = new ArrayList<>();
		for (String hostKeyString : serverHostKey.split(SERVER_HOST_KEY_SEPARATOR)) {
			hostKeyTypes.add(HostKeyType.byTypeString(hostKeyString));
		}
		return hostKeyTypes;
	}

	public static void configureGlobalSettings() {
		reconfigurePreferredAuthentications();
		registerLogger();
	}
}