package notification

import (
	// External
	werr "git.ahall.se/go/wrappederror"

	// Standard
	"encoding/json"
	"log/slog"
	"net/url"
	"os/exec"
	"strconv"
	"strings"
)

type Script struct {
	Filename       string
	Prio           int
	AcknowledgeURL string
	logger         *slog.Logger

	exists  bool
	updated Service
}

func init() {
	allServices = append(allServices, &Script{})
}

func NewScript(config []byte, prio int, ackURL string) (instance *Script, err error) {
	instance = new(Script)
	err = json.Unmarshal(config, &instance)
	if err != nil {
		err = werr.Wrap(err).WithCode("002-0001").WithData(config)
		return
	}
	instance.Prio = prio
	instance.AcknowledgeURL = ackURL
	return instance, nil
}

func (script *Script) SetLogger(l *slog.Logger) {
	script.logger = l
}

func (script *Script) GetType() string {
	return "SCRIPT"
}

func (script *Script) GetPrio() int {
	return script.Prio
}

func (script *Script) SetPrio(prio int) {
	script.Prio = prio
}

func (script *Script) SetExists(exists bool) {
	script.exists = exists
}

func (script Script) Exists() bool {
	return script.exists
}

func (script *Script) String() string {
	return script.Filename
}

func (script Script) Send(problemID int, msg []byte) (err error) {
	var errbuf strings.Builder
	cmd := exec.Command(script.Filename, strconv.Itoa(problemID), script.AcknowledgeURL, string(msg))
	cmd.Stderr = &errbuf

	err = cmd.Run()
	if err != nil {
		script.logger.Error("notification", "type", "script", "error", err)
		err = werr.Wrap(err).WithData(
			struct {
				Filename  string
				ProblemID int
				Msg       string
				StdErr    string
			}{
				script.Filename,
				problemID,
				string(msg),
				errbuf.String(),
			},
		).Log()
	}

	return
}

func (script *Script) Update(values url.Values) (err error) {
	updated := Script{}
	script.updated = &updated

	updated.Prio, err = strconv.Atoi(values.Get("prio"))
	if err != nil {
		return werr.Wrap(err)
	}

	givenFilename := values.Get("filename")
	if strings.TrimSpace(givenFilename) == "" {
		return werr.New("Filename cannot be empty")
	}
	updated.Filename = strings.TrimSpace(givenFilename)
	return
}

func (script *Script) Updated() Service {
	return script.updated
}

func (script *Script) Commit() {
	updated := script.updated.(*Script)
	script.Prio = updated.Prio
	script.Filename = updated.Filename
}

func (script Script) JSON() []byte {
	data := struct {
		Filename string `json:"filename"`
	}{
		script.Filename,
	}
	j, _ := json.Marshal(data)
	return j
}
