package api

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

	"party.at/party/cmd/party/common"
	"party.at/party/internal/data"
	"party.at/party/internal/validator"
)

type Api struct {
	App *common.Application
}

type contextKey string

const userKey contextKey = "user"

func SetUser(r *http.Request, user *data.User) *http.Request {
	return r.WithContext(context.WithValue(r.Context(), userKey, user))
}

func GetUser(r *http.Request) *data.User {
	user, ok := r.Context().Value(userKey).(*data.User)
	if !ok {
		panic("missing user value in request context")
	}
	return user
}

func (api *Api) Authenticate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Vary", "Authorization")

		authorizationHeader := r.Header.Get("Authorization")
		if authorizationHeader == "" {
			r = SetUser(r, data.AnonymousUser)
			next.ServeHTTP(w, r)
			return
		}

		headerParts := strings.Split(authorizationHeader, " ")
		if len(headerParts) != 2 || headerParts[0] != "Bearer" {
			api.InvalidAuthenticationTokenResponse(w, r)
			return
		}

		token := headerParts[1]
		v := validator.New()
		if data.ValidateTokenPlaintext(v, token); !v.Valid() {
			api.InvalidAuthenticationTokenResponse(w, r)
			return
		}

		userIdentity, err := api.App.Models.UserIdentities.GetForToken(data.ScopeAuthentication, token)
		if err != nil {
			switch {
			case errors.Is(err, data.ErrRecordNotFound):
				api.InvalidAuthenticationTokenResponse(w, r)
			default:
				api.ServerErrorResponse(w, r, err)
			}
			return
		}

		user, err := api.App.Models.Users.Get(userIdentity.UserID)
		if err != nil {
			switch {
			case errors.Is(err, data.ErrRecordNotFound):
				api.InvalidCredentialsResponse(w, r)
			default:
				api.ServerErrorResponse(w, r, err)
			}
			return
		}

		r = SetUser(r, user)
		next.ServeHTTP(w, r)
	})
}
