package rules

import (
	"net/http"
	"net/url"
)

type (
	FieldHandler struct {
		set, add, remove CommandHandler
	}
	FieldModifier string
)

const (
	ModFieldSet    FieldModifier = "set"
	ModFieldAdd    FieldModifier = "add"
	ModFieldRemove FieldModifier = "remove"
)

const (
	FieldHeader = "header"
	FieldQuery  = "query"
	FieldCookie = "cookie"
)

var modFields = map[string]struct {
	help     Help
	validate ValidateFunc
	builder  func(args any) *FieldHandler
}{
	FieldHeader: {
		help: Help{
			command: FieldHeader,
			args: map[string]string{
				"key":   "the header key",
				"value": "the header value",
			},
		},
		validate: toStrTuple,
		builder: func(args any) *FieldHandler {
			k, v := args.(*StrTuple).Unpack()
			return &FieldHandler{
				set: StaticCommand(func(w http.ResponseWriter, r *http.Request) {
					w.Header()[k] = []string{v}
				}),
				add: StaticCommand(func(w http.ResponseWriter, r *http.Request) {
					h := w.Header()
					h[k] = append(h[k], v)
				}),
				remove: StaticCommand(func(w http.ResponseWriter, r *http.Request) {
					delete(w.Header(), k)
				}),
			}
		},
	},
	FieldQuery: {
		help: Help{
			command: FieldQuery,
			args: map[string]string{
				"key":   "the query key",
				"value": "the query value",
			},
		},
		validate: toStrTuple,
		builder: func(args any) *FieldHandler {
			k, v := args.(*StrTuple).Unpack()
			return &FieldHandler{
				set: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateQueries(r, func(queries url.Values) {
						queries.Set(k, v)
					})
					return true
				}),
				add: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateQueries(r, func(queries url.Values) {
						queries.Add(k, v)
					})
					return true
				}),
				remove: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateQueries(r, func(queries url.Values) {
						queries.Del(k)
					})
					return true
				}),
			}
		},
	},
	FieldCookie: {
		help: Help{
			command: FieldCookie,
			args: map[string]string{
				"key":   "the cookie key",
				"value": "the cookie value",
			},
		},
		validate: toStrTuple,
		builder: func(args any) *FieldHandler {
			k, v := args.(*StrTuple).Unpack()
			return &FieldHandler{
				set: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
						for i, c := range cookies {
							if c.Name == k {
								cookies[i].Value = v
								return cookies
							}
						}
						return append(cookies, &http.Cookie{Name: k, Value: v})
					})
					return true
				}),
				add: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
						return append(cookies, &http.Cookie{Name: k, Value: v})
					})
					return true
				}),
				remove: DynamicCommand(func(cached Cache, w http.ResponseWriter, r *http.Request) bool {
					cached.UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
						index := -1
						for i, c := range cookies {
							if c.Name == k {
								index = i
								break
							}
						}
						if index != -1 {
							if len(cookies) == 1 {
								return []*http.Cookie{}
							}
							return append(cookies[:index], cookies[index+1:]...)
						}
						return cookies
					})
					return true
				}),
			}
		},
	},
}