package be.iminds.ilabt.jfed.util;

import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnection;
import be.iminds.ilabt.jfed.lowlevel.ssh_key_info.GeniUserSshKeyInfo;
import be.iminds.ilabt.jfed.lowlevel.ssh_key_info.SshKeyInfoFactory;
import be.iminds.ilabt.jfed.rspec.model.LoginService;
import be.iminds.ilabt.jfed.rspec.model.ProxyService;
import be.iminds.ilabt.jfed.rspec.model.RspecNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
 * This class provides utility methods to finding the correct Proxy-information to do a login.
 * <p>
 * <p>
 * User: twalcari
 * Date: 9/10/2015
 * Time: 10:23
 */
@Singleton
public class ProxyServiceUtil {
    private static final Logger LOG = LoggerFactory.getLogger(ProxyServiceUtil.class);

    private final GeniUserProvider geniUserProvider;

    @Inject
    public ProxyServiceUtil(GeniUserProvider geniUserProvider) {
        this.geniUserProvider = geniUserProvider;
    }


    public JFedConnection.SshProxyInfo findTestbedProxy(@Nonnull RspecNode node, @Nonnull LoginService loginService) {
        assert node.getLoginServices().contains(loginService);

        if (node.getProxyServices().isEmpty()) {
            LOG.debug("findTestbedProxy: node has no login proxy info");
            return null;
        }
        LOG.debug("findTestbedProxy: node has " + node.getProxyServices().size() + " login proxy info");

        String loginServiceString = loginServiceToString(loginService);

        for (ProxyService proxyService : node.getProxyServices()) {
            LOG.debug("findTestbedProxy: checking if " + loginServiceString + " matches proxy for " + proxyService.getFor() + " (proxy=" + proxyService.getProxy() + ")");
            if (Objects.equals(loginServiceString, proxyService.getFor())) {
                assert proxyService.getProxy() != null;

                return stringToProxyInfo(proxyService.getProxy(), proxyService.getHostKey());
            }
        }

        return null;
    }

    public static List<LoginService> nonProxyLoginServices(@Nonnull RspecNode manifestNode) {
        List<LoginService> loginServices = manifestNode.getLoginServices();
        LOG.debug("nonProxyLoginServices: There are " + loginServices.size() + " proxies.");

        if (manifestNode.getProxyServices().isEmpty())
            return loginServices;

        for (ProxyService proxyService : manifestNode.getProxyServices()) {
            for (Iterator<LoginService> it = loginServices.iterator(); it.hasNext(); ) {
                LoginService curLs = it.next();

                if (Objects.equals(loginServiceToString(curLs), proxyService.getProxy())) {
                    LOG.debug("nonProxyLoginServices: Ignoring login service " + loginServiceToString(curLs) + " because it is a proxy.");
                    it.remove();
                }
            }
        }

        return loginServices;
    }

    private static String loginServiceToString(@Nonnull LoginService loginService) {
        return loginService.getUsername() + "@" + loginService.getHostname() + ":" + loginService.getPort();
    }

    private JFedConnection.SshProxyInfo stringToProxyInfo(@Nonnull String proxyString, @Nullable String hostKey) {
        final String originalProxyString = proxyString;

        String username;
        String hostname;
        int port = 22;

        int atPos = proxyString.indexOf("@");

        if (atPos >= 0) {
            username = proxyString.substring(0, atPos);
        } else {
            LOG.warn("No username could be found in proxy-string '{}'", originalProxyString);
            username = null;
        }

        proxyString = proxyString.substring(atPos + 1);
        int semiColonPos = proxyString.lastIndexOf(":");

        if (semiColonPos >= 0) {
            try {
                port = Integer.parseInt(proxyString.substring(semiColonPos + 1));
            } catch (NumberFormatException ex) {
                LOG.error("Could not properly parse port number in proxy-string '{}'", originalProxyString);
                port = 22;
            }

            hostname = proxyString.substring(0, semiColonPos);
        } else {
            LOG.warn("No port number could be found in proxy-string '{}'", originalProxyString);
            hostname = proxyString;
        }

        final GeniUserSshKeyInfo sshKeyInfo =
                SshKeyInfoFactory.createGeniUserSshKeyInfo(geniUserProvider.getLoggedInGeniUser());
        return new JFedConnection.SshProxyInfo(hostname, port, username, sshKeyInfo, hostKey, false);

    }

}
