package handlers

import (
	"context"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"

	"veza-backend-api/internal/services"
)

// RBACServiceInterface defines the interface for RBAC operations
// This allows for easier testing with mocks
type RBACServiceInterface interface {
	CreateRole(ctx context.Context, name, description string, permissions []uuid.UUID) (*services.Role, error)
	GetRoleByID(ctx context.Context, roleID uuid.UUID) (*services.Role, error)
	GetAllRoles(ctx context.Context) ([]*services.Role, error)
	AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error
	RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error
	GetUserRoles(ctx context.Context, userID uuid.UUID) ([]*services.Role, error)
	GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]services.Permission, error)
	CheckPermission(ctx context.Context, userID uuid.UUID, resource, action string) (bool, error)
	CreatePermission(ctx context.Context, name, description, resource, action string) (*services.Permission, error)
}

// RBACHandlers handles RBAC-related API endpoints
type RBACHandlers struct {
	rbacService RBACServiceInterface
	logger      *zap.Logger
}

// NewRBACHandlers creates new RBAC handlers
func NewRBACHandlers(rbacService *services.RBACService, logger *zap.Logger) *RBACHandlers {
	return &RBACHandlers{
		rbacService: rbacService,
		logger:      logger,
	}
}

// NewRBACHandlersWithInterface creates new RBAC handlers with an interface (for testing)
func NewRBACHandlersWithInterface(rbacService RBACServiceInterface, logger *zap.Logger) *RBACHandlers {
	return &RBACHandlers{
		rbacService: rbacService,
		logger:      logger,
	}
}

// InitRBACHandlers initializes RBAC handlers
func InitRBACHandlers(rbacService *services.RBACService, logger *zap.Logger) {
	handlers := NewRBACHandlers(rbacService, logger)

	// Store handlers globally for route registration
	RBACHandlersInstance = handlers
}

// RBACHandlersInstance holds the global RBAC handlers instance
var RBACHandlersInstance *RBACHandlers

// CreateRole creates a new role
func (h *RBACHandlers) CreateRole(c *gin.Context) {
	var req struct {
		Name        string      `json:"name" binding:"required"`
		Description string      `json:"description"`
		Permissions []uuid.UUID `json:"permissions"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	role, err := h.rbacService.CreateRole(c.Request.Context(), req.Name, req.Description, req.Permissions)
	if err != nil {
		h.logger.Error("Failed to create role", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create role"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"success": true,
		"role":    role,
	})
}

// GetRole gets a role by ID
func (h *RBACHandlers) GetRole(c *gin.Context) {
	roleID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID"})
		return
	}

	role, err := h.rbacService.GetRoleByID(c.Request.Context(), roleID)
	if err != nil {
		h.logger.Error("Failed to get role", zap.Error(err))
		c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"role":    role,
	})
}

// GetAllRoles gets all roles
func (h *RBACHandlers) GetAllRoles(c *gin.Context) {
	roles, err := h.rbacService.GetAllRoles(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get roles", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get roles"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"roles":   roles,
	})
}

// AssignRoleToUser assigns a role to a user
func (h *RBACHandlers) AssignRoleToUser(c *gin.Context) {
	userID, err := uuid.Parse(c.Param("user_id"))
	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"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	err = h.rbacService.AssignRoleToUser(c.Request.Context(), userID, req.RoleID)
	if err != nil {
		h.logger.Error("Failed to assign role to user", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to assign role to user"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Role assigned to user successfully",
	})
}

// RemoveRoleFromUser removes a role from a user
func (h *RBACHandlers) RemoveRoleFromUser(c *gin.Context) {
	userID, err := uuid.Parse(c.Param("user_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
		return
	}

	roleID, err := uuid.Parse(c.Param("role_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID"})
		return
	}

	err = h.rbacService.RemoveRoleFromUser(c.Request.Context(), userID, roleID)
	if err != nil {
		h.logger.Error("Failed to remove role from user", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove role from user"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Role removed from user successfully",
	})
}

// GetUserRoles gets all roles for a user
func (h *RBACHandlers) GetUserRoles(c *gin.Context) {
	userID, err := uuid.Parse(c.Param("user_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
		return
	}

	roles, err := h.rbacService.GetUserRoles(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get user roles", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user roles"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"roles":   roles,
	})
}

// GetUserPermissions gets all permissions for a user
func (h *RBACHandlers) GetUserPermissions(c *gin.Context) {
	userID, err := uuid.Parse(c.Param("user_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
		return
	}

	permissions, err := h.rbacService.GetUserPermissions(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get user permissions", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user permissions"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success":     true,
		"permissions": permissions,
	})
}

// CheckPermission checks if a user has a specific permission
func (h *RBACHandlers) CheckPermission(c *gin.Context) {
	userID, err := uuid.Parse(c.Param("user_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
		return
	}

	resource := c.Query("resource")
	action := c.Query("action")

	if resource == "" || action == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Resource and action are required"})
		return
	}

	hasPermission, err := h.rbacService.CheckPermission(c.Request.Context(), userID, resource, action)
	if err != nil {
		h.logger.Error("Failed to check permission", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check permission"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success":        true,
		"has_permission": hasPermission,
		"resource":       resource,
		"action":         action,
	})
}

// CreatePermission creates a new permission
func (h *RBACHandlers) CreatePermission(c *gin.Context) {
	var req struct {
		Name        string `json:"name" binding:"required"`
		Description string `json:"description"`
		Resource    string `json:"resource" binding:"required"`
		Action      string `json:"action" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	permission, err := h.rbacService.CreatePermission(c.Request.Context(), req.Name, req.Description, req.Resource, req.Action)
	if err != nil {
		h.logger.Error("Failed to create permission", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create permission"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"success":    true,
		"permission": permission,
	})
}
