package management

import (
	"context"
	"fmt"
	"net/http"
)

type ctxKey int

const (
	accessClaimsCtxKey ctxKey = iota
)

var errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}

// HTTP middleware setting the parsed access_token claims in the request context
func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Validate access token
		accessToken := r.URL.Query().Get("access_token")
		if accessToken == "" {
			writeHTTPErrorResponse(w, errMissingAccessToken)
			return
		}
		token, err := ParseToken(accessToken)
		if err != nil {
			writeHTTPErrorResponse(w, errMissingAccessToken)
			return
		}
		r = r.WithContext(context.WithValue(r.Context(), accessClaimsCtxKey, token))
		next.ServeHTTP(w, r)
	})
}

// Middleware validation error struct for returning to the eyeball
type managementError struct {
	Code    int    `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

func (m *managementError) Error() string {
	return m.Message
}

// Middleware validation error HTTP response JSON for returning to the eyeball
type managementErrorResponse struct {
	Success bool              `json:"success,omitempty"`
	Errors  []managementError `json:"errors,omitempty"`
}

// writeErrorResponse will respond to the eyeball with basic HTTP JSON payloads with validation failure information
func writeHTTPErrorResponse(w http.ResponseWriter, errResp managementError) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusBadRequest)
	err := json.NewEncoder(w).Encode(managementErrorResponse{
		Success: false,
		Errors:  []managementError{errResp},
	})
	// we have already written the header, so write a basic error response if unable to encode the error
	if err != nil {
		// fallback to text message
		http.Error(w, fmt.Sprintf(
			"%d %s",
			http.StatusBadRequest,
			http.StatusText(http.StatusBadRequest),
		), http.StatusBadRequest)
	}
}
