package notification

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

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

type Pushover struct {
	Description string
	UserKey     string `json:"user_key"`
	APIKey      string `json:"api_key"`
	DeviceName  string `json:"device_name"`

	Prio           int
	AcknowledgeURL string
	logger         *slog.Logger

	exists  bool
	updated Service
}

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

func NewPushover(config []byte, prio int, ackURL string) (instance *Pushover, err error) {
	instance = new(Pushover)
	err = json.Unmarshal(config, &instance)
	if err != nil {
		err = werr.Wrap(err).WithData(config)
		return
	}
	instance.Prio = prio
	instance.AcknowledgeURL = ackURL
	return instance, nil
}

func (po *Pushover) SetLogger(l *slog.Logger) {
	po.logger = l
}

func (po *Pushover) GetType() string {
	return "PUSHOVER"
}

func (po *Pushover) GetPrio() int {
	return po.Prio
}

func (po *Pushover) SetPrio(prio int) {
	po.Prio = prio
}

func (po *Pushover) SetExists(exists bool) {
	po.exists = exists
}

func (po Pushover) Exists() bool {
	return po.exists
}

func (po *Pushover) String() string {
	if po.Description != "" {
		return po.Description
	}

	return fmt.Sprintf("%s, %s", po.UserKey, po.APIKey)
}

func (po Pushover) Send(problemID int, msg []byte) (err error) {
	var req *http.Request
	var res *http.Response

	pushoverRequest, _ := json.Marshal(map[string]string{
		"token":   po.APIKey,
		"user":    po.UserKey,
		"device":  po.DeviceName,
		"message": string(msg),
	})

	req, err = http.NewRequest("POST", "https://api.pushover.net/1/messages.json", bytes.NewReader(pushoverRequest))
	if err != nil {
		err = werr.Wrap(err).WithData(struct {
			UserKey string
			APIKey  string
			Msg     []byte
		}{
			po.UserKey,
			po.APIKey,
			msg,
		},
		)
		return
	}

	//ackURL := fmt.Sprintf("http, OK, %s/notification/ack?problemID=%d", po.AcknowledgeURL, problemID)
	req.Header.Add("Content-Type", "application/json")
	res, err = http.DefaultClient.Do(req)
	if err != nil {
		err = werr.Wrap(err)
		return
	}

	body, _ := io.ReadAll(res.Body)
	poResp := struct {
		Status int
		Errors []string
	}{}
	err = json.Unmarshal(body, &poResp)
	if err != nil {
		err = werr.Wrap(err).WithData(body)
		return
	}

	if poResp.Status != 1 {
		err = werr.New("%s", strings.Join(poResp.Errors, ", "))
		return
	}

	if res.StatusCode != 200 {
		err = werr.New("Invalid Pushover response").WithData(body)
		return
	}

	return
}

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

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

	// Description
	updated.Description = strings.TrimSpace(values.Get("description"))

	// API (application) key
	givenAPIKey := values.Get("api_key")
	if strings.TrimSpace(givenAPIKey) == "" {
		return werr.New("API key cannot be empty")
	}
	updated.APIKey = strings.TrimSpace(givenAPIKey)

	// User key
	givenUserKey := values.Get("user_key")
	if strings.TrimSpace(givenUserKey) == "" {
		return werr.New("User key cannot be empty")
	}
	updated.UserKey = strings.TrimSpace(givenUserKey)

	// Device name
	updated.DeviceName = strings.TrimSpace(values.Get("device_name"))
	return
}

func (po *Pushover) Updated() Service {
	return po.updated
}

func (po *Pushover) Commit() {
	updatedPushover := po.updated.(*Pushover)
	po.Prio = updatedPushover.Prio
	po.Description = updatedPushover.Description
	po.APIKey = updatedPushover.APIKey
	po.UserKey = updatedPushover.UserKey
	po.DeviceName = updatedPushover.DeviceName
}

func (po Pushover) JSON() []byte {
	data := struct {
		Description string
		APIKey      string `json:"api_key"`
		UserKey     string `json:"user_key"`
		DeviceName  string `json:"device_name"`
	}{
		po.Description,
		po.APIKey,
		po.UserKey,
		po.DeviceName,
	}
	j, _ := json.Marshal(data)
	return j
}
