package handlers

import (
	"net/http"

	"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// ============================================================================
// Role Endpoints
// ============================================================================

// ListRoles returns roles for a tenant (including system roles)
func (h *RBACHandlers) ListRoles(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	var tenantIDPtr *uuid.UUID
	if tenantID != uuid.Nil {
		tenantIDPtr = &tenantID
	}

	roles, err := h.store.ListRoles(c.Request.Context(), tenantIDPtr)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

// ListSystemRoles returns all system roles
func (h *RBACHandlers) ListSystemRoles(c *gin.Context) {
	roles, err := h.store.ListSystemRoles(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 returns a role by ID
func (h *RBACHandlers) GetRole(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role ID"})
		return
	}

	role, err := h.store.GetRole(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
		return
	}

	c.JSON(http.StatusOK, role)
}

// CreateRole creates a new role
func (h *RBACHandlers) CreateRole(c *gin.Context) {
	var role rbac.Role
	if err := c.ShouldBindJSON(&role); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tenantID := rbac.GetTenantID(c)
	if tenantID != uuid.Nil {
		role.TenantID = &tenantID
	}

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

	c.JSON(http.StatusCreated, role)
}

// ============================================================================
// User Role Endpoints
// ============================================================================

// AssignRoleRequest represents a role assignment request
type AssignRoleRequest struct {
	UserID      string  `json:"user_id" binding:"required"`
	RoleID      string  `json:"role_id" binding:"required"`
	NamespaceID *string `json:"namespace_id"`
	ExpiresAt   *string `json:"expires_at"` // RFC3339 format
}

// AssignRole assigns a role to a user
func (h *RBACHandlers) AssignRole(c *gin.Context) {
	var req AssignRoleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID, err := uuid.Parse(req.UserID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user ID"})
		return
	}

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

	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	grantorID := rbac.GetUserID(c)
	if grantorID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	userRole := &rbac.UserRole{
		UserID:   userID,
		RoleID:   roleID,
		TenantID: tenantID,
	}

	if req.NamespaceID != nil {
		nsID, err := uuid.Parse(*req.NamespaceID)
		if err == nil {
			userRole.NamespaceID = &nsID
		}
	}

	if err := h.service.AssignRoleToUser(c.Request.Context(), userRole, grantorID); err != nil {
		if err == rbac.ErrPermissionDenied {
			c.JSON(http.StatusForbidden, gin.H{"error": "permission denied"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "role assigned successfully"})
}

// RevokeRole revokes a role from a user
func (h *RBACHandlers) RevokeRole(c *gin.Context) {
	userIDStr := c.Param("userId")
	roleIDStr := c.Param("roleId")

	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user ID"})
		return
	}

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

	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	revokerID := rbac.GetUserID(c)
	if revokerID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	var namespaceID *uuid.UUID
	if nsIDStr := c.Query("namespace_id"); nsIDStr != "" {
		if nsID, err := uuid.Parse(nsIDStr); err == nil {
			namespaceID = &nsID
		}
	}

	if err := h.service.RevokeRoleFromUser(c.Request.Context(), userID, roleID, tenantID, namespaceID, revokerID); err != nil {
		if err == rbac.ErrPermissionDenied {
			c.JSON(http.StatusForbidden, gin.H{"error": "permission denied"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "role revoked successfully"})
}

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

	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

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

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

// ============================================================================
// Permission Endpoints
// ============================================================================

// GetEffectivePermissions returns effective permissions for the current user
func (h *RBACHandlers) GetEffectivePermissions(c *gin.Context) {
	userID := rbac.GetUserID(c)
	tenantID := rbac.GetTenantID(c)
	namespaceID := rbac.GetNamespaceID(c)

	if userID == uuid.Nil || tenantID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	perms, err := h.service.GetEffectivePermissions(c.Request.Context(), userID, tenantID, namespaceID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, perms)
}

// GetUserContext returns complete context for the current user
func (h *RBACHandlers) GetUserContext(c *gin.Context) {
	userID := rbac.GetUserID(c)
	tenantID := rbac.GetTenantID(c)

	if userID == uuid.Nil || tenantID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	ctx, err := h.policyEngine.GetUserContext(c.Request.Context(), userID, tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, ctx)
}

// CheckPermission checks if user has a specific permission
func (h *RBACHandlers) CheckPermission(c *gin.Context) {
	permission := c.Query("permission")
	if permission == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "permission parameter required"})
		return
	}

	userID := rbac.GetUserID(c)
	tenantID := rbac.GetTenantID(c)
	namespaceID := rbac.GetNamespaceID(c)

	if userID == uuid.Nil || tenantID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	hasPermission, err := h.service.HasPermission(c.Request.Context(), userID, tenantID, namespaceID, permission)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"permission":     permission,
		"has_permission": hasPermission,
	})
}

// ============================================================================
// LLM Policy Endpoints
// ============================================================================

// ListLLMPolicies returns LLM policies for a tenant
func (h *RBACHandlers) ListLLMPolicies(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	policies, err := h.store.ListLLMPolicies(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"policies": policies})
}

// GetLLMPolicy returns an LLM policy by ID
func (h *RBACHandlers) GetLLMPolicy(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid policy ID"})
		return
	}

	policy, err := h.store.GetLLMPolicy(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "policy not found"})
		return
	}

	c.JSON(http.StatusOK, policy)
}

// CreateLLMPolicy creates a new LLM policy
func (h *RBACHandlers) CreateLLMPolicy(c *gin.Context) {
	var policy rbac.LLMPolicy
	if err := c.ShouldBindJSON(&policy); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	policy.TenantID = tenantID
	if err := h.store.CreateLLMPolicy(c.Request.Context(), &policy); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, policy)
}

// UpdateLLMPolicy updates an LLM policy
func (h *RBACHandlers) UpdateLLMPolicy(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid policy ID"})
		return
	}

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

	policy.ID = id
	if err := h.store.UpdateLLMPolicy(c.Request.Context(), &policy); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, policy)
}

// DeleteLLMPolicy deletes an LLM policy
func (h *RBACHandlers) DeleteLLMPolicy(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid policy ID"})
		return
	}

	if err := h.store.DeleteLLMPolicy(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "policy deleted"})
}
