package ifttt

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

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

// Config holds settings for the IFTTT notification service.
type Config struct {
	standard.EnumlessConfig

	WebHookID         string   `required:"true" url:"host"`
	Events            []string `required:"true"            key:"events"`
	Value1            string   `                           key:"value1"       optional:""`
	Value2            string   `                           key:"value2"       optional:""`
	Value3            string   `                           key:"value3"       optional:""`
	UseMessageAsValue uint8    `                           key:"messagevalue"             default:"2" desc:"Sets the corresponding value field to the notification message"`
	UseTitleAsValue   uint8    `                           key:"titlevalue"               default:"0" desc:"Sets the corresponding value field to the notification title"`
	Title             string   `                           key:"title"                    default:""  desc:"Notification title, optionally set by the sender"`
}

const (
	Scheme              = "ifttt" // Scheme identifies this service in configuration URLs.
	DefaultMessageValue = 2       // Default value field (1-3) for the notification message
	DisabledValue       = 0       // Value to disable title assignment
	MinValueField       = 1       // Minimum valid value field (Value1)
	MaxValueField       = 3       // Maximum valid value field (Value3)
	MinLength           = 1       // Minimum length for required fields like Events and WebHookID
)

var (
	ErrInvalidMessageValue = errors.New(
		"invalid value for messagevalue: only values 1-3 are supported",
	)
	ErrInvalidTitleValue = errors.New(
		"invalid value for titlevalue: only values 1-3 or 0 (for disabling) are supported",
	)
	ErrTitleMessageConflict = errors.New("titlevalue cannot use the same number as messagevalue")
	ErrMissingEvents        = errors.New("events missing from config URL")
	ErrMissingWebhookID     = errors.New("webhook ID missing from config URL")
)

// GetURL generates a URL from the current configuration values.
func (c *Config) GetURL() *url.URL {
	resolver := format.NewPropKeyResolver(c)

	return c.getURL(&resolver)
}

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

	return c.setURL(&resolver, serviceURL)
}

func (c *Config) getURL(resolver types.ConfigQueryResolver) *url.URL {
	return &url.URL{
		Host:     c.WebHookID,
		Path:     "/",
		Scheme:   Scheme,
		RawQuery: format.BuildQuery(resolver),
	}
}

func (c *Config) setURL(resolver types.ConfigQueryResolver, serviceURL *url.URL) error {
	if c.UseMessageAsValue == DisabledValue {
		c.UseMessageAsValue = DefaultMessageValue
	}

	c.WebHookID = serviceURL.Hostname()

	for key, vals := range serviceURL.Query() {
		if err := resolver.Set(key, vals[0]); err != nil {
			return fmt.Errorf("setting config property %q from URL query: %w", key, err)
		}
	}

	if c.UseMessageAsValue > MaxValueField || c.UseMessageAsValue < MinValueField {
		return ErrInvalidMessageValue
	}

	if c.UseTitleAsValue > MaxValueField {
		return ErrInvalidTitleValue
	}

	if c.UseTitleAsValue != DisabledValue &&
		c.UseTitleAsValue == c.UseMessageAsValue {
		return ErrTitleMessageConflict
	}

	if serviceURL.String() != "ifttt://dummy@dummy.com" {
		if len(c.Events) < MinLength {
			return ErrMissingEvents
		}

		if len(c.WebHookID) < MinLength {
			return ErrMissingWebhookID
		}
	}

	return nil
}
