package server

import (
	"encoding/base64"
	"net/http"
	"strconv"
	"strings"
	"sync"

	"github.com/gin-gonic/gin"
	"github.com/photoprism/photoprism/internal/entity"
)

var basicAuth = struct {
	user  map[string]entity.User
	mutex sync.RWMutex
}{user: make(map[string]entity.User)}

func GetCredentials(c *gin.Context) (username, password, raw string) {
	data := c.GetHeader("Authorization")

	if !strings.HasPrefix(data, "Basic ") {
		return "", "", data
	}

	data = strings.TrimPrefix(data, "Basic ")

	auth, err := base64.StdEncoding.DecodeString(data)

	if err != nil {
		return "", "", data
	}

	credentials := strings.SplitN(string(auth), ":", 2)

	if len(credentials) != 2 {
		return "", "", data
	}

	return credentials[0], credentials[1], data
}

func BasicAuth() gin.HandlerFunc {
	realm := "Authorization Required"
	realm = "Basic realm=" + strconv.Quote(realm)

	return func(c *gin.Context) {
		invalid := true

		username, password, raw := GetCredentials(c)

		basicAuth.mutex.Lock()
		defer basicAuth.mutex.Unlock()

		if user, ok := basicAuth.user[raw]; ok {
			c.Set(gin.AuthUserKey, user.UserUID)
			return
		}

		user := entity.FindUserByName(username)

		if user != nil {
			invalid = user.InvalidPassword(password)
		}

		if user == nil || invalid {
			c.Header("WWW-Authenticate", realm)
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		basicAuth.user[raw] = *user

		c.Set(gin.AuthUserKey, user.UserUID)
	}
}
