package utils

import (
	"fmt"
	"github.com/gofiber/fiber/v2"
	"strings"

	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)

const (
	prefix = "Bearer "
)

func NewAuthenticator(jwtUtil *JWT) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if jwtUtil == nil {
			return fiber.NewError(fiber.StatusInternalServerError, errors.ErrInvalidArgs.Error())
		}

		err := authenticate(jwtUtil, c)
		if err != nil {
			return fiber.NewError(fiber.StatusUnauthorized, err.Error())
		}

		return c.Next()
	}
}

func authenticate(jwtUtil *JWT, c *fiber.Ctx) error {
	jws, err := parseJWSFromRequest(c)
	if err != nil {
		return err
	}

	userID, validateErr := jwtUtil.Validate(jws)
	if validateErr != nil {
		return validateErr
	}

	c.Locals(models.AuthJWTDecodedUserIDKey, userID)
	c.Locals(models.AuthJWTDecodedAccessTokenKey, jws)

	return nil
}

func parseJWSFromRequest(c *fiber.Ctx) (string, error) {
	header := c.Get("Authorization")

	if header != "" && strings.HasPrefix(header, prefix) {
		return strings.TrimPrefix(header, prefix), nil
	}

	token := c.Query("Authorization")
	if token == "" {
		return "", errors.New(
			fmt.Errorf("failed to parse jws from request: no valid token found"),
			errors.ErrNoAccess,
		)
	}

	return token, nil
}
