package additions

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

	"github.com/prometheus/procfs"
	log "github.com/sirupsen/logrus"
)

type system struct {
	// procfs handle
	pfs procfs.FS
}

// NewSystem() Initalizes OS specific interfaces
// Linux:
//   - Access to procfs
func NewSystem() (System, error) {
	newProcFS, err := procfs.NewFS("/proc")

	if err != nil {
		return nil, err
	}

	return &system{
		pfs: newProcFS,
	}, nil
}

func (sys *system) SetHostname(hostname *string) error {
	// TODO: potentially some safety checks?
	return syscall.Sethostname([]byte(*hostname))
}

func (sys *system) GetFreeMemory() uint64 {
	memInfo, err := sys.pfs.Meminfo()
	if err != nil {
		// TODO: better error handling is required
		log.Error("GetTotalMemory ", err)
		return 0
	}
	return *memInfo.MemFree
}

func (sys *system) GetTotalMemory() uint64 {

	memInfo, err := sys.pfs.Meminfo()
	if err != nil {
		// TODO: better error handling is required
		log.Error("GetTotalMemory ", err)
		return 0
	}

	return *memInfo.MemTotal
}

func (sys *system) GetUsedMemory() uint64 {
	memInfo, err := sys.pfs.Meminfo()
	if err != nil {
		// TODO: better error handling is required
		log.Error("GetTotalMemory ", err)
		return 0
	}

	return *memInfo.MemTotal - *memInfo.MemFree
}

func (sys *system) GetSoftwareVersion() (string, error) {

	var returnString string
	var osID string
	var osVersion string

	// open corresponding file with OS version in it
	// seems to be /etc/os-release on Linux
	osVersionFile, err := os.Open("/etc/os-release")
	if err != nil {
		return "", err
	}

	fileRdr := bufio.NewScanner((osVersionFile))
	fileRdr.Split(bufio.ScanLines)

	// go line by line and look for
	// ID and VERSION_ID entries
	// this version may result in empty or incomplete information
	// TODO: better error handling while parsing the file.
	for fileRdr.Scan() {
		strElement := strings.FieldsFunc(fileRdr.Text(), func(r rune) bool {
			if r == '=' {
				return true
			} else if r == '"' {
				return true
			}
			return false
		})
		if strElement[0] == "ID" {
			osID = strElement[1]
		}

		if strElement[0] == "VERSION_ID" {
			osVersion = strElement[1]
		}
	}

	returnString = osID + ":" + osVersion

	return returnString, nil
}

func (sys *system) GetMotd() (string, error) {

	motdMessage, err := os.ReadFile("/etc/motd")
	if err == nil {
		return string(motdMessage), nil
	}

	cmd := exec.Command("run-parts", "/etc/update-motd.d/")
	motdMessage, err = cmd.Output()
	if err != nil {
		return "", err
	}

	return string(motdMessage), nil
}

func (sys *system) SetMotd(message string) error {
	return os.WriteFile("/etc/motd", []byte(message), 0644)
}