package manager

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"text/template"

	"git.rootprojects.org/root/go-serviceman/manager/static"
	"git.rootprojects.org/root/go-serviceman/service"
)

const (
	srvExt      = ".plist"
	srvSysPath  = "/Library/LaunchDaemons"
	srvUserPath = "Library/LaunchAgents"
)

var srvLen int

func init() {
	srvLen = len(srvExt)
}

func start(conf *service.Service) error {
	system := conf.System
	home := conf.Home
	rdns := conf.ReverseDNS

	service, err := getService(system, home, rdns)
	if nil != err {
		return err
	}

	cmds := []Runnable{
		Runnable{
			Exec: "launchctl",
			Args: []string{"unload", "-w", service},
			Must: false,
		},
		Runnable{
			Exec:     "launchctl",
			Args:     []string{"load", "-w", service},
			Must:     true,
			Badwords: []string{"No such file or directory", "service already loaded"},
		},
	}

	cmds = adjustPrivs(system, cmds)

	typ := "USER"
	if system {
		typ = "SYSTEM"
	}
	fmt.Printf("Starting launchd %s service...\n\n", typ)
	for i := range cmds {
		exe := cmds[i]
		fmt.Println("\t" + exe.String())
		err := exe.Run()
		if nil != err {
			return err
		}
	}
	fmt.Println()

	return nil
}

func stop(conf *service.Service) error {
	system := conf.System
	home := conf.Home
	rdns := conf.ReverseDNS

	service, err := getService(system, home, rdns)
	if nil != err {
		return err
	}

	cmds := []Runnable{
		Runnable{
			Exec:     "launchctl",
			Args:     []string{"unload", service},
			Must:     false,
			Badwords: []string{"No such file or directory", "Cound not find specified service"},
		},
	}

	cmds = adjustPrivs(system, cmds)

	fmt.Println()
	typ := "USER"
	if system {
		typ = "SYSTEM"
	}
	fmt.Printf("Stopping launchd %s service...\n", typ)
	for i := range cmds {
		exe := cmds[i]
		fmt.Println("\t" + exe.String())
		err := exe.Run()
		if nil != err {
			return err
		}
	}
	fmt.Println()

	return nil
}

// Render will create a launchd .plist file using the simple internal template
func Render(c *service.Service) ([]byte, error) {
	// Create service file from template
	b, err := static.ReadFile("dist/Library/LaunchDaemons/_rdns_.plist.tmpl")
	if err != nil {
		return nil, err
	}
	s := string(b)
	rw := &bytes.Buffer{}
	// not sure what the template name does, but whatever
	tmpl, err := template.New("service").Parse(s)
	if err != nil {
		return nil, err
	}
	err = tmpl.Execute(rw, c)
	if nil != err {
		return nil, err
	}

	return rw.Bytes(), nil
}

func install(c *service.Service) (string, error) {
	// Darwin-specific config options
	if c.PrivilegedPorts {
		if !c.System {
			return "", fmt.Errorf("You must use root-owned LaunchDaemons (not user-owned LaunchAgents) to use priveleged ports on OS X")
		}
	}
	plistDir := srvSysPath
	if !c.System {
		plistDir = filepath.Join(c.Home, srvUserPath)
	}

	// Check paths first
	err := os.MkdirAll(plistDir, 0755)
	if nil != err {
		return "", err
	}

	b, err := Render(c)
	if nil != err {
		return "", err
	}

	// Write the file out
	// TODO rdns
	plistName := c.ReverseDNS + ".plist"
	plistPath := filepath.Join(plistDir, plistName)
	if err := ioutil.WriteFile(plistPath, b, 0644); err != nil {
		return "", fmt.Errorf("Error writing %s: %v", plistPath, err)
	}

	// TODO --no-start
	err = start(c)
	if nil != err {
		fmt.Printf("If things don't go well you should be able to get additional logging from launchctl:\n")
		fmt.Printf("\tsudo launchctl log level debug\n")
		fmt.Printf("\ttail -f /var/log/system.log\n")
		return "", err
	}

	return "launchd", nil
}
