package handlers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"
)

// RoleHandler gère les endpoints de gestion des rôles
type RoleHandler struct {
	roleService   *services.RoleService
	commonHandler *CommonHandler
}

// NewRoleHandler crée un nouveau RoleHandler
func NewRoleHandler(roleService *services.RoleService, logger *zap.Logger) *RoleHandler {
	return &RoleHandler{
		roleService:   roleService,
		commonHandler: NewCommonHandler(logger),
	}
}

// GetRoles récupère tous les rôles
func (h *RoleHandler) GetRoles(c *gin.Context) {
	roles, err := h.roleService.GetRoles(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"roles": roles})
}

// GetRole récupère un rôle par ID
func (h *RoleHandler) GetRole(c *gin.Context) {
	roleIDStr := c.Param("id")
	roleID, err := uuid.Parse(roleIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
		return
	}

	role, err := h.roleService.GetRole(c.Request.Context(), roleID)
	if err != nil {
		if err.Error() == "role not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}
	c.JSON(http.StatusOK, gin.H{"role": role})
}

// CreateRole crée un nouveau rôle
func (h *RoleHandler) CreateRole(c *gin.Context) {
	var role models.Role
	if appErr := h.commonHandler.BindAndValidateJSON(c, &role); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	if err := h.roleService.CreateRole(c.Request.Context(), &role); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"role": role})
}

// UpdateRole met à jour un rôle
func (h *RoleHandler) UpdateRole(c *gin.Context) {
	roleIDStr := c.Param("id")
	roleID, err := uuid.Parse(roleIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
		return
	}

	var updates models.Role
	if appErr := h.commonHandler.BindAndValidateJSON(c, &updates); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	if err := h.roleService.UpdateRole(c.Request.Context(), roleID, &updates); err != nil {
		if err.Error() == "role not found or is system role" {
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "role updated"})
}

// DeleteRole supprime un rôle
func (h *RoleHandler) DeleteRole(c *gin.Context) {
	roleIDStr := c.Param("id")
	roleID, err := uuid.Parse(roleIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
		return
	}

	if err := h.roleService.DeleteRole(c.Request.Context(), roleID); err != nil {
		if err.Error() == "role not found" || err.Error() == "cannot delete system role" {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "role deleted"})
}

// AssignRole assigne un rôle à un utilisateur
func (h *RoleHandler) AssignRole(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"})
		return
	}

	var req struct {
		RoleID    uuid.UUID  `json:"role_id" binding:"required"`
		ExpiresAt *time.Time `json:"expires_at"`
	}
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Récupérer l'ID de l'utilisateur qui assigne depuis le contexte
	assignedByInterface, exists := c.Get("user_id")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
		return
	}

	assignedBy, ok := assignedByInterface.(uuid.UUID)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user id type"})
		return
	}

	if err := h.roleService.AssignRoleToUser(c.Request.Context(), userID, req.RoleID, assignedBy, req.ExpiresAt); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "role assigned"})
}

// RevokeRole révoque un rôle d'un utilisateur
func (h *RoleHandler) RevokeRole(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"})
		return
	}

	roleIDStr := c.Param("roleId")
	roleID, err := uuid.Parse(roleIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
		return
	}

	if err := h.roleService.RevokeRoleFromUser(c.Request.Context(), userID, roleID); err != nil {
		if err.Error() == "role assignment not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "role revoked"})
}

// GetUserRoles récupère tous les rôles d'un utilisateur
func (h *RoleHandler) GetUserRoles(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"})
		return
	}

	roles, err := h.roleService.GetUserRoles(c.Request.Context(), userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"roles": roles})
}
