package api

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/photoprism/photoprism/internal/auth/acl"
	"github.com/photoprism/photoprism/internal/config/customize"
	"github.com/photoprism/photoprism/internal/entity"
	"github.com/photoprism/photoprism/internal/photoprism/get"
	"github.com/photoprism/photoprism/pkg/i18n"
)

// GetSettings returns the user app settings as JSON.
//
//	@Summary	returns the user app settings as JSON
//	@Id			GetSettings
//	@Tags		Settings
//	@Produce	json
//	@Success	200			{object}	customize.Settings
//	@Failure	401,403,404	{object}	i18n.Response
//	@Router		/api/v1/settings [get]
func GetSettings(router *gin.RouterGroup) {
	router.GET("/settings", func(c *gin.Context) {
		s := AuthAny(c, acl.ResourceSettings, acl.Permissions{acl.AccessAll, acl.AccessOwn})

		// Abort if permission is not granted.
		if s.Abort(c) {
			return
		}

		settings := get.Config().SessionSettings(s)

		if settings == nil {
			Abort(c, http.StatusNotFound, i18n.ErrNotFound)
			return
		}

		c.JSON(http.StatusOK, settings)
	})
}

// SaveSettings saves the user app settings.
//
//	@Summary	saves the user app settings
//	@Id			SaveSettings
//	@Tags		Settings
//	@Accept		json
//	@Produce	json
//	@Success	200					{object}	customize.Settings
//	@Failure	400,401,403,404,500	{object}	i18n.Response
//	@Param		settings			body		customize.Settings	true	"user settings"
//	@Router		/api/v1/settings [post]
func SaveSettings(router *gin.RouterGroup) {
	router.POST("/settings", func(c *gin.Context) {
		s := AuthAny(c, acl.ResourceSettings, acl.Permissions{acl.ActionView, acl.ActionUpdate, acl.ActionManage})

		// Abort if permission is not granted.
		if s.Abort(c) {
			return
		}

		conf := get.Config()

		// Settings disabled?
		if conf.DisableSettings() {
			AbortForbidden(c)
			return
		}

		var settings *customize.Settings

		user := s.GetUser()

		// Update user preferences without changing global defaults.
		if user == nil {
			AbortUnexpectedError(c)
			return
		}

		// Only super admins without scope restrictions can change global config defaults.
		if user.IsSuperAdmin() && s.NoScope() {
			// Update global defaults and user preferences.
			settings = conf.Settings()

			// Set values from request.
			if err := c.BindJSON(settings); err != nil {
				AbortBadRequest(c, err)
				return
			}

			// Update global defaults.
			if err := settings.Save(conf.SettingsYaml()); err != nil {
				log.Debugf("config: %s (save app settings)", err)
				c.AbortWithStatusJSON(http.StatusInternalServerError, err)
				return
			}

			// Update user preferences.
			if err := user.Settings().Apply(settings).Save(); err != nil {
				log.Debugf("config: %s (save user settings)", err)
				AbortSaveFailed(c)
				return
			}

			// Flush session cache and update client config
			// after global settings have been updated.
			entity.FlushSessionCache()
			UpdateClientConfig()
		} else {
			settings = &customize.Settings{}

			// Set values from request.
			if err := c.BindJSON(settings); err != nil {
				AbortBadRequest(c, err)
				return
			}

			// Update user preferences.
			if acl.Rules.DenyAll(acl.ResourceSettings, s.GetUserRole(), acl.Permissions{acl.ActionUpdate, acl.ActionManage}) {
				c.JSON(http.StatusOK, user.Settings().Apply(settings).ApplyTo(conf.Settings().ApplyACL(acl.Rules, user.AclRole())))
				return
			} else if err := user.Settings().Apply(settings).Save(); err != nil {
				log.Debugf("config: %s (save user settings)", err)
				AbortSaveFailed(c)
				return
			}
		}

		// Return updated user settings.
		c.JSON(http.StatusOK, get.Config().SessionSettings(s))
	})
}
