// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package auth

import (
	"context"
	"fmt"

	"github.com/bufbuild/connect-go"
)

// NewServerInterceptor creates a server-side interceptor which validates that an incoming request contains a Bearer Authorization header
func NewServerInterceptor() connect.UnaryInterceptorFunc {
	interceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
		return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {

			if req.Spec().IsClient {
				return next(ctx, req)
			}

			token, err := tokenFromRequest(ctx, req)
			if err != nil {
				return nil, err
			}

			return next(TokenToContext(ctx, token), req)
		})
	}

	return connect.UnaryInterceptorFunc(interceptor)
}

func tokenFromRequest(ctx context.Context, req connect.AnyRequest) (Token, error) {
	headers := req.Header()

	bearerToken, err := BearerTokenFromHeaders(headers)
	if err == nil {
		return NewAccessToken(bearerToken), nil
	}

	cookie := req.Header().Get("Cookie")
	if cookie != "" {
		return NewCookieToken(cookie), nil
	}

	return Token{}, connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("No access token or cookie credentials available on request."))
}

// NewClientInterceptor creates a client-side interceptor which injects token as a Bearer Authorization header
func NewClientInterceptor(accessToken string) connect.UnaryInterceptorFunc {
	interceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
		return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {

			if !req.Spec().IsClient {
				return next(ctx, req)
			}

			ctx = TokenToContext(ctx, NewAccessToken(accessToken))

			req.Header().Add(authorizationHeaderKey, bearerPrefix+accessToken)
			return next(ctx, req)
		})
	}

	return connect.UnaryInterceptorFunc(interceptor)
}
