package fuphttp

import (
	"crypto/subtle"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

func tokenFromRequest(r *http.Request) (token string, ok bool) {
	// Check for a Fup-Token header.
	v := r.Header.Get("Fup-Token")
	if v != "" {
		return v, true
	}

	// Check for basic auth.
	_, v, ok = r.BasicAuth()
	return v, ok
}

func TokenAuthMiddleware(token, realm string) mux.MiddlewareFunc {
	return func(next http.Handler) http.Handler {
		if token == "" {
			return next
		}
		tokenBytes := []byte(token)

		return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
			ctx := r.Context()
			if !IsMutate(ctx) {
				// Allow all access to public pages.
				next.ServeHTTP(rw, r)
				return
			}

			requestAuth := func(s string) {
				rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q, charset=\"UTF-8\"", realm))
				http.Error(rw, s, http.StatusUnauthorized)
			}

			pw, ok := tokenFromRequest(r)
			switch {
			case !ok:
				requestAuth("unparsable or no credentials")
				return
			case subtle.ConstantTimeCompare([]byte(pw), tokenBytes) != 1:
				requestAuth("bad credentials")
				return
			}

			// Auth check passed, let's go.
			next.ServeHTTP(rw, r)
			return
		})
	}
}