package runner

import (
	"code.icod.de/dalu/gomanager/ent"
	"context"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

type Runner struct {
	client *ent.Client
}

func NewRunner(client *ent.Client) *Runner {
	r := new(Runner)
	r.client = client
	return r
}

// Run checks all projects sequentially
func (r *Runner) Run() error {
	ms, e := r.client.Project.Query().All(context.Background())
	if e != nil {
		return e
	}
	if len(ms) == 0 {
		return nil
	}
	v, e := getCurrentGoVersion()
	if e != nil {
		return e
	}
	for _, m := range ms {
		fv, e := getVersionOfFile(fmt.Sprintf("%s/%s", m.RootPath, m.BinaryPath))
		if e != nil {
			return e
		}
		if v != fv {
			l, e := goBuildBinary(m.RootPath)
			if e != nil {
				return e
			}
			if e := chownBinary(m.User, m.Group, fmt.Sprintf("%s/%s", m.RootPath, m.BinaryPath)); e != nil {
				return e
			}
			_, e = r.client.Logentry.
				Create().
				SetContent(l).
				Save(context.Background())
			if e != nil {
				return e
			}
		}
	}
	return nil
}

// getCurrentGoVersion returns the version of the installed go package or a non-nil error
func getCurrentGoVersion() (string, error) {
	cmd := exec.Command("go", "version")
	var out strings.Builder
	cmd.Stdout = &out
	if e := cmd.Run(); e != nil {
		return "", e
	}
	split := strings.Split(out.String(), " ")
	version := split[2]
	return version, nil
}

// getVersionOfFile returns the version of a go binary or an non-nil error
func getVersionOfFile(file string) (string, error) {
	cmd := exec.Command("go", "version", file)
	var out strings.Builder
	cmd.Stdout = &out
	if e := cmd.Run(); e != nil {
		return "", e
	}
	split := strings.Split(out.String(), " ")
	version := split[1]
	return version, nil
}

// goBuildBinary does a go build in the root directory, then "cd"s back to the previous working directory.
func goBuildBinary(root string) (string, error) {
	cwd, e := os.Getwd()
	if e != nil {
		return "", e
	}
	if e := os.Chdir(root); e != nil {
		return "", e
	}

	cmd := exec.Command("go", "build")
	var out strings.Builder
	cmd.Stdout = &out
	if e := cmd.Run(); e != nil {
		return "", e
	}
	if e := os.Chdir(cwd); e != nil {
		return "", e
	}
	return out.String(), nil
}

func chownBinary(user, group, file string) error {
	cmd := exec.Command("chown", fmt.Sprintf("%s:%s", user, group), file)
	return cmd.Run()
}
