package api

import (
	"errors"
	"net/http"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/auth"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/services"
	"github.com/gin-gonic/gin"
)

func listInstanceUsers(c *gin.Context) {
	users, err := services.ListUsers(auth.InstanceID(c))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, users)
}

func actorMayGrantOwner(c *gin.Context) bool {
	return auth.Role(c) == models.RoleOwner
}

func createInstanceUser(c *gin.Context) {
	var body struct {
		Email    string `json:"email"`
		Password string `json:"password"`
		Role     string `json:"role"`
	}
	if err := c.ShouldBindJSON(&body); err != nil || body.Email == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "email required"})
		return
	}
	if body.Role == "" {
		body.Role = models.RoleMember
	}
	if !models.ValidRole(body.Role) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "role must be one of owner, admin, or member"})
		return
	}
	if body.Role == models.RoleOwner && !actorMayGrantOwner(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "only an owner can create another owner"})
		return
	}
	u, err := services.CreateUser(auth.InstanceID(c), body.Email, body.Password, body.Role, "local")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, u)
}

func updateInstanceUserRole(c *gin.Context) {
	var body struct {
		Role string `json:"role"`
	}
	if err := c.ShouldBindJSON(&body); err != nil || body.Role == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "role required"})
		return
	}
	if !models.ValidRole(body.Role) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "role must be one of owner, admin, or member"})
		return
	}

	instanceID, targetID := auth.InstanceID(c), c.Param("id")
	if targetID == auth.UserID(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "you cannot change your own role"})
		return
	}
	target, err := services.GetUserInInstance(instanceID, targetID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}
	if (body.Role == models.RoleOwner || target.Role == models.RoleOwner) && !actorMayGrantOwner(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "only an owner can change owner roles"})
		return
	}

	if err := services.UpdateUserRole(instanceID, targetID, body.Role); err != nil {
		c.JSON(orgUserErrStatus(err), gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

func deleteInstanceUser(c *gin.Context) {
	instanceID, targetID := auth.InstanceID(c), c.Param("id")
	if targetID == auth.UserID(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "you cannot remove your own account"})
		return
	}
	target, err := services.GetUserInInstance(instanceID, targetID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}
	if target.Role == models.RoleOwner && !actorMayGrantOwner(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "only an owner can remove another owner"})
		return
	}

	if err := services.DeleteUser(instanceID, targetID); err != nil {
		c.JSON(orgUserErrStatus(err), gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"deleted": true})
}

func orgUserErrStatus(err error) int {
	// 409 rather than 403: the caller has the right to manage members, and the
	// request is refused because of the resource's state, not their permissions.
	if errors.Is(err, services.ErrLastOwner) || errors.Is(err, services.ErrHQManaged) {
		return http.StatusConflict
	}
	return http.StatusInternalServerError
}
