package ntfy

import (
	"errors"
	"fmt"
	"net/url"
	"strings"

	"github.com/nicholas-fedor/shoutrrr/pkg/format"
	"github.com/nicholas-fedor/shoutrrr/pkg/types"
)

// Config holds the configuration for the Ntfy service.
type Config struct {
	Title                  string   `default:""        desc:"Message title"                                                                                    key:"title"`
	Host                   string   `default:"ntfy.sh" desc:"Server hostname and port"                                                                                                      url:"host"`
	Topic                  string   `                  desc:"Target topic name"                                                                                                             url:"path"     required:""`
	Password               string   `                  desc:"Auth password"                                                                                                                 url:"password"             optional:""`
	Username               string   `                  desc:"Auth username"                                                                                                                 url:"user"                 optional:""`
	Scheme                 string   `default:"https"   desc:"Server protocol, http or https"                                                                   key:"scheme"`
	Tags                   []string `                  desc:"List of tags that may or not map to emojis"                                                       key:"tags"                                              optional:""`
	Priority               priority `default:"default" desc:"Message priority with 1=min, 3=default and 5=max"                                                 key:"priority"`
	Actions                []string `                  desc:"Custom user action buttons for notifications, see https://docs.ntfy.sh/publish/#action-buttons"   key:"actions"                                           optional:"" sep:";"`
	Click                  string   `                  desc:"Website opened when notification is clicked"                                                      key:"click"                                             optional:""`
	Attach                 string   `                  desc:"URL of an attachment, see attach via URL"                                                         key:"attach"                                            optional:""`
	Filename               string   `                  desc:"File name of the attachment"                                                                      key:"filename"                                          optional:""`
	Delay                  string   `                  desc:"Timestamp or duration for delayed delivery, see https://docs.ntfy.sh/publish/#scheduled-delivery" key:"delay,at,in"                                       optional:""`
	Email                  string   `                  desc:"E-mail address for e-mail notifications"                                                          key:"email"                                             optional:""`
	Icon                   string   `                  desc:"URL to use as notification icon"                                                                  key:"icon"                                              optional:""`
	Markdown               bool     `default:"no"      desc:"Enable Markdown formatting, see https://docs.ntfy.sh/publish/#markdown-formatting"                key:"markdown"`
	Cache                  bool     `default:"yes"     desc:"Cache messages"                                                                                   key:"cache"`
	Firebase               bool     `default:"yes"     desc:"Send to firebase"                                                                                 key:"firebase"`
	DisableTLSVerification bool     `default:"no"      desc:"Disable TLS certificate verification"                                                             key:"disabletlsverification"`
	DisableTLS             bool     `default:"no"      desc:"Disable TLS entirely (force HTTP scheme)"                                                         key:"disabletls"`
}

// Scheme is the identifying part of this service's configuration URL.
const (
	Scheme = "ntfy"
)

// ErrTopicRequired indicates that the topic is missing from the config URL.
var ErrTopicRequired = errors.New("topic is required")

// Enums returns the fields that use an EnumFormatter for their values.
func (*Config) Enums() map[string]types.EnumFormatter {
	return map[string]types.EnumFormatter{
		"Priority": Priority.Enum,
	}
}

// GetAPIURL constructs the API URL for the Ntfy service based on the configuration.
func (c *Config) GetAPIURL() string {
	path := c.Topic
	if !strings.HasPrefix(c.Topic, "/") {
		path = "/" + path
	}

	apiURL := url.URL{
		Scheme: c.Scheme,
		Host:   c.Host,
		Path:   path,
	}

	return apiURL.String()
}

// GetURL returns a URL representation of the Config's current field values.
func (c *Config) GetURL() *url.URL {
	resolver := format.NewPropKeyResolver(c)

	return c.getURL(&resolver)
}

// QueryFields returns the list of query parameter names for the Config struct.
func (c *Config) QueryFields() []string {
	return format.GetConfigQueryResolver(c).QueryFields()
}

// SetURL updates the Config from a URL representation of its field values.
func (c *Config) SetURL(serviceURL *url.URL) error {
	resolver := format.NewPropKeyResolver(c)

	return c.setURL(&resolver, serviceURL)
}

// getURL constructs a URL from the Config's fields using the provided resolver.
func (c *Config) getURL(resolver types.ConfigQueryResolver) *url.URL {
	path := c.Topic
	if !strings.HasPrefix(c.Topic, "/") {
		path = "/" + path
	}

	result := &url.URL{
		Host:       c.Host,
		Scheme:     Scheme,
		ForceQuery: true,
		Path:       path,
		RawQuery:   format.BuildQuery(resolver),
	}
	if c.Username != "" {
		if c.Password != "" {
			result.User = url.UserPassword(c.Username, c.Password)
		} else {
			result.User = url.User(c.Username)
		}
	}

	return result
}

// setURL updates the Config from a URL using the provided resolver.
func (c *Config) setURL(resolver types.ConfigQueryResolver, serviceURL *url.URL) error {
	if serviceURL.User != nil {
		password, _ := serviceURL.User.Password()
		c.Password = password
		c.Username = serviceURL.User.Username()
	} else {
		c.Password = ""
		c.Username = ""
	}

	c.Host = serviceURL.Host
	c.Topic = strings.TrimPrefix(serviceURL.Path, "/")

	serviceURL.RawQuery = strings.ReplaceAll(serviceURL.RawQuery, ";", "%3b")
	for key, vals := range serviceURL.Query() {
		if err := resolver.Set(key, vals[0]); err != nil {
			return fmt.Errorf("setting query parameter %q to %q: %w", key, vals[0], err)
		}
	}

	if serviceURL.String() != "ntfy://dummy@dummy.com" {
		if c.Topic == "" {
			return ErrTopicRequired
		}
	}

	return nil
}
