package api

import (
	"errors"
	"net/http"

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

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 {
	if errors.Is(err, services.ErrLastOwner) {
		return http.StatusConflict
	}
	return http.StatusInternalServerError
}

func getInstanceOIDC(c *gin.Context) {
	cfg, err := services.GetInstanceOIDC(auth.InstanceID(c))
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"enabled": false, "client_secret_set": false})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"instance_id":       cfg.InstanceID,
		"issuer":            cfg.Issuer,
		"client_id":         cfg.ClientID,
		"enabled":           cfg.Enabled,
		"updated_at":        cfg.UpdatedAt,
		"client_secret_set": cfg.ClientSecretEnc != "",
	})
}

func putInstanceOIDC(c *gin.Context) {
	var body struct {
		Issuer       string `json:"issuer"`
		ClientID     string `json:"client_id"`
		ClientSecret string `json:"client_secret"`
		Enabled      bool   `json:"enabled"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := services.SaveInstanceOIDC(auth.InstanceID(c), body.Issuer, body.ClientID, body.ClientSecret, body.Enabled); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	auth.EvictOIDCProvider(auth.InstanceID(c))
	c.JSON(http.StatusOK, gin.H{"saved": true})
}
