// Defines a wrapper around the system ping binary.
//
// NOTE(os): may fail with non-unix compliant system ping binaries.
package ping

import (
	"errors"
	"os/exec"
	"strconv"
	"strings"
)

// runPing returns the byte array as returned by [exec.Command]
//
// NOTE(os): this function may fail on non-unix compliant implementations of the Ping spec.
func runPing(address string) (delay *[]byte, err error) {
	out, err := exec.Command("ping", address, "-c 1").Output()
	if err != nil {
		return nil, err
	}
	return &out, err
}

// splitBytesToLines splits bytes as returned by [pingstats.runPing] into an
// array of strings by newline
func splitBytesToLines(bytes *[]byte) (lines []string) {
	return strings.Split(strings.ReplaceAll(string(*bytes), "\r\n", "\n"), "\n")
}

// Ping returns the delay of a single Ping as reported by the system Ping binary.
//
// If the function is unable to resolve the system binary output or fails to
// successfully resolve a Ping, it will always return -1.
//
// NOTE(os): this function may fail on non-unix compliant implementations of the Ping spec.
func Ping(address string) (delay float64, err error) {
	out, err := runPing(address)
	if err != nil {
		return -1, err
	}

	lines := splitBytesToLines(out)

	for i := 0; i < len(lines); i++ {
		if strings.Contains(lines[i], "bytes from") {
			position := strings.Index(lines[i], "time=")

			before, _, success := strings.Cut(lines[i][position:], " ")
			if !success {
				return -1, errors.New("Line does not match pattern!")
			}
			_, after, success := strings.Cut(before, "=")
			if !success {
				return -1, errors.New("Line does not match pattern!")
			}

			delay, _ := strconv.ParseFloat(after, 64)
			return delay, nil
		}
	}
	return -1, errors.New("could not resolve host: " + address)
}
