package server

import (
	"context"
	"net/http"
	"strings"

	"github.com/influxdata/chronograf"
)

// CookieExtractor extracts the token from the value of the Name cookie.
type CookieExtractor struct {
	Name string
}

// Extract returns the value of cookie Name
func (c *CookieExtractor) Extract(r *http.Request) (string, error) {
	cookie, err := r.Cookie(c.Name)
	if err != nil {
		return "", chronograf.ErrAuthentication
	}
	return cookie.Value, nil
}

// BearerExtractor extracts the token from Authorization: Bearer header.
type BearerExtractor struct{}

// Extract returns the string following Authorization: Bearer
func (b *BearerExtractor) Extract(r *http.Request) (string, error) {
	s := r.Header.Get("Authorization")
	if s == "" {
		return "", chronograf.ErrAuthentication
	}

	// Check for Bearer token.
	strs := strings.Split(s, " ")

	if len(strs) != 2 || strs[0] != "Bearer" {
		return "", chronograf.ErrAuthentication
	}
	return strs[1], nil
}

// AuthorizedToken extracts the token and validates; if valid the next handler
// will be run.  The principal will be sent to the next handler via the request's
// Context.  It is up to the next handler to determine if the principal has access.
// On failure, will return http.StatusUnauthorized.
func AuthorizedToken(auth chronograf.Authenticator, te chronograf.TokenExtractor, logger chronograf.Logger, next http.Handler) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log := logger.
			WithField("component", "auth").
			WithField("remote_addr", r.RemoteAddr).
			WithField("method", r.Method).
			WithField("url", r.URL)

		token, err := te.Extract(r)
		if err != nil {
			log.Error("Unable to extract token")
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		// We do not check the validity of the principal.  Those
		// server further down the chain should do so.
		principal, err := auth.Authenticate(r.Context(), token)
		if err != nil {
			log.Error("Invalid token")
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		// Send the principal to the next handler
		ctx := context.WithValue(r.Context(), chronograf.PrincipalKey, principal)
		next.ServeHTTP(w, r.WithContext(ctx))
		return
	})
}