package common

import (
	"bytes"
	"errors"
	"os"
	"os/exec"
	"regexp"
)

// IfconfigIPFinder finds the host IP based on the output of `ip address` or `ifconfig`.
type IfconfigIPFinder struct {
	Device string
}

func (f *IfconfigIPFinder) HostIP() (string, error) {
	ip, err := ipaddress(f.Device)
	if err != nil || ip == "" {
		return ifconfig(f.Device)
	}
	return ip, err
}

func ipaddress(device string) (string, error) {
	var ipPath string

	// On some systems, ip is in /sbin which is generally not
	// on the PATH for a standard user, so we just check that first.
	if _, err := os.Stat("/sbin/ip"); err == nil {
		ipPath = "/sbin/ip"
	}

	if ipPath == "" {
		var err error
		ipPath, err = exec.LookPath("ip")
		if err != nil {
			return "", err
		}
	}

	stdout := new(bytes.Buffer)
	cmd := exec.Command(ipPath, "address", "show", "dev", device)
	// Force LANG=C so that the output is what we expect it to be
	// despite the locale.
	cmd.Env = append(cmd.Env, "LANG=C")
	cmd.Env = append(cmd.Env, os.Environ()...)

	cmd.Stdout = stdout
	cmd.Stderr = new(bytes.Buffer)
	if err := cmd.Run(); err != nil {
		return "", err
	}

	re := regexp.MustCompile(`inet[^\d]+([\d\.]+)/`)
	matches := re.FindStringSubmatch(stdout.String())
	if matches == nil {
		return "", errors.New("IP not found in ip a output...")
	}

	return matches[1], nil
}

func ifconfig(device string) (string, error) {
	var ifconfigPath string

	// On some systems, ifconfig is in /sbin which is generally not
	// on the PATH for a standard user, so we just check that first.
	if _, err := os.Stat("/sbin/ifconfig"); err == nil {
		ifconfigPath = "/sbin/ifconfig"
	}

	if ifconfigPath == "" {
		var err error
		ifconfigPath, err = exec.LookPath("ifconfig")
		if err != nil {
			return "", err
		}
	}

	stdout := new(bytes.Buffer)

	cmd := exec.Command(ifconfigPath, device)
	// Force LANG=C so that the output is what we expect it to be
	// despite the locale.
	cmd.Env = append(cmd.Env, "LANG=C")
	cmd.Env = append(cmd.Env, os.Environ()...)

	cmd.Stdout = stdout
	cmd.Stderr = new(bytes.Buffer)
	if err := cmd.Run(); err != nil {
		return "", err
	}

	re := regexp.MustCompile(`inet[^\d]+([\d\.]+)\s`)
	matches := re.FindStringSubmatch(stdout.String())
	if matches == nil {
		return "", errors.New("IP not found in ifconfig output...")
	}

	return matches[1], nil
}