package ssh

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/sirupsen/logrus"
)

// HostConfig is a SSH host config.
type HostConfig struct {
	Host         string
	IdentityFile string
	Port         string
	User         string
}

// String presents a human friendly output for the HostConfig.
func (h HostConfig) String() string {
	return fmt.Sprintf(
		"{host: %s, username: %s, port: %s, identityfile: %s}",
		h.Host,
		h.User,
		h.Port,
		h.IdentityFile,
	)
}

// GetHostConfig retrieves a ~/.ssh/config config for a host using /usr/bin/ssh
// directly. We therefore maintain consistent interop with this standard
// tooling. This is useful because SSH confuses a lot of people and having to
// learn how two tools (`ssh` and `abra`) handle SSH connection details instead
// of one (just `ssh`) is Not Cool. Here's to less bug reports on this topic!
func GetHostConfig(hostname string) (HostConfig, error) {
	var hostConfig HostConfig

	out, err := exec.Command("ssh", "-G", hostname).Output()
	if err != nil {
		return hostConfig, err
	}

	for _, line := range strings.Split(string(out), "\n") {
		entries := strings.Split(line, " ")
		for idx, entry := range entries {
			if entry == "hostname" {
				hostConfig.Host = entries[idx+1]
			}
			if entry == "user" {
				hostConfig.User = entries[idx+1]
			}
			if entry == "port" {
				hostConfig.Port = entries[idx+1]
			}
			if entry == "identityfile" {
				if hostConfig.IdentityFile == "" {
					hostConfig.IdentityFile = entries[idx+1]
				}
			}
		}
	}

	logrus.Debugf("retrieved ssh config for %s: %s", hostname, hostConfig.String())

	return hostConfig, nil
}

// Fatal is a error output wrapper which aims to make SSH failures easier to
// parse through re-wording.
func Fatal(hostname string, err error) error {
	out := err.Error()
	if strings.Contains(out, "Host key verification failed.") {
		return fmt.Errorf("SSH host key verification failed for %s", hostname)
	} else if strings.Contains(out, "Could not resolve hostname") {
		return fmt.Errorf("could not resolve hostname for %s", hostname)
	} else if strings.Contains(out, "Connection timed out") {
		return fmt.Errorf("connection timed out for %s", hostname)
	} else if strings.Contains(out, "Permission denied") {
		return fmt.Errorf("ssh auth: permission denied for %s", hostname)
	} else if strings.Contains(out, "Network is unreachable") {
		return fmt.Errorf("unable to connect to %s, network is unreachable?", hostname)
	} else {
		return err
	}
}