package notification

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

	// Standard
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type NTFY struct {
	URL            string
	Prio           int
	AcknowledgeURL string
	logger         *slog.Logger

	exists  bool
	updated Service
}

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

func NewNTFY(config []byte, prio int, ackURL string) (instance *NTFY, err error) {
	instance = new(NTFY)
	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 (ntfy *NTFY) SetLogger(l *slog.Logger) {
	ntfy.logger = l
}

func (ntfy *NTFY) GetType() string {
	return "NTFY"
}

func (ntfy *NTFY) GetPrio() int {
	return ntfy.Prio
}

func (ntfy *NTFY) SetPrio(prio int) {
	ntfy.Prio = prio
}

func (ntfy *NTFY) SetExists(exists bool) {
	ntfy.exists = exists
}

func (ntfy NTFY) Exists() bool {
	return ntfy.exists
}

func (ntfy *NTFY) String() string {
	return ntfy.URL
}

func (ntfy NTFY) Send(problemID int, msg []byte) (err error) {
	var req *http.Request
	var res *http.Response
	req, err = http.NewRequest("POST", ntfy.URL, bytes.NewReader(msg))
	if err != nil {
		err = werr.Wrap(err).WithCode("002-0002").WithData(ntfy.URL)
		return
	}

	ackURL := fmt.Sprintf("http, OK, %s/notification/ack?problemID=%d", ntfy.AcknowledgeURL, problemID)
	req.Header.Add("X-Actions", ackURL)
	req.Header.Add("X-Priority", "4") // XXX: should be 5
	req.Header.Add("X-Tags", "calendar")

	res, err = http.DefaultClient.Do(req)
	if err != nil {
		err = werr.Wrap(err).WithCode("002-0003")
		return
	}

	body, _ := io.ReadAll(res.Body)
	if res.StatusCode != 200 {
		err = werr.New("Invalid NTFY response").WithCode("002-0004").WithData(body)
		return
	}

	ntfyResp := struct {
		ID string
	}{}
	err = json.Unmarshal(body, &ntfyResp)
	if err != nil {
		err = werr.Wrap(err).WithCode("002-0005").WithData(body)
		return
	}

	return
}

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

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

	givenURL := values.Get("url")
	if strings.TrimSpace(givenURL) == "" {
		return werr.New("URL cannot be empty")
	}
	updated.URL = strings.TrimSpace(givenURL)
	return
}

func (ntfy *NTFY) Updated() Service {
	return ntfy.updated
}

func (ntfy *NTFY) Commit() {
	updatedNTFY := ntfy.updated.(*NTFY)
	ntfy.Prio = updatedNTFY.Prio
	ntfy.URL = updatedNTFY.URL
}

func (ntfy NTFY) JSON() []byte {
	data := struct {
		URL string `json:"url"`
	}{
		ntfy.URL,
	}
	j, _ := json.Marshal(data)
	return j
}
