package ldap

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/http"
)

const (
	HTTPHeaderUser   = "Twitch-Ldap-User"
	HTTPHeaderGroups = "Twitch-Ldap-Groups"
)

var (
	ErrUnknownUser        = errors.New("ldap user unknown")
	ErrUnknownGroups      = errors.New("ldap groups unknown")
	ErrMalformattedGroups = errors.New("ldap groups malformatted")
)

// Middleware to extract the LDAP user and groups from the supplied
// HTTP headers and inject them into the request context.
func Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		user, err := extractUser(r.Header)
		if err != nil {
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}
		r = r.WithContext(WithUser(r.Context(), user))

		groups, err := extractGroups(r.Header)
		if err != nil {
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}
		r = r.WithContext(WithGroups(r.Context(), groups))

		next.ServeHTTP(w, r)
	})
}

func extractUser(h http.Header) (string, error) {
	user := h.Get(HTTPHeaderUser)
	if user == "" {
		return "", ErrUnknownUser
	}
	return user, nil
}

// decode base64 encoded json formated ldap group list
func extractGroups(h http.Header) ([]string, error) {
	raw := h.Get(HTTPHeaderGroups)
	if raw == "" {
		return nil, ErrUnknownGroups
	}
	groupsJSON, err := base64.StdEncoding.DecodeString(raw)
	if err != nil {
		return nil, ErrMalformattedGroups
	}
	var groups []string
	err = json.Unmarshal(groupsJSON, &groups)
	if err != nil {
		return nil, ErrMalformattedGroups
	}
	return groups, nil
}
