package iam

import (
	"database/sql"
	"fmt"
	"net/http"
	"strings"

	"github.com/atlasos/calypso/internal/common/config"
	"github.com/atlasos/calypso/internal/common/database"
	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/atlasos/calypso/internal/common/password"
	"github.com/gin-gonic/gin"
)

// Handler handles IAM-related requests
type Handler struct {
	db     *database.DB
	config *config.Config
	logger *logger.Logger
}

// NewHandler creates a new IAM handler
func NewHandler(db *database.DB, cfg *config.Config, log *logger.Logger) *Handler {
	return &Handler{
		db:     db,
		config: cfg,
		logger: log,
	}
}

// ListUsers lists all users
func (h *Handler) ListUsers(c *gin.Context) {
	query := `
		SELECT id, username, email, full_name, is_active, is_system,
		       created_at, updated_at, last_login_at
		FROM users
		ORDER BY username
	`

	rows, err := h.db.Query(query)
	if err != nil {
		h.logger.Error("Failed to list users", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list users"})
		return
	}
	defer rows.Close()

	var users []map[string]interface{}
	for rows.Next() {
		var u struct {
			ID          string
			Username    string
			Email       string
			FullName    string
			IsActive    bool
			IsSystem    bool
			CreatedAt   string
			UpdatedAt   string
			LastLoginAt *string
		}
		if err := rows.Scan(&u.ID, &u.Username, &u.Email, &u.FullName,
			&u.IsActive, &u.IsSystem, &u.CreatedAt, &u.UpdatedAt, &u.LastLoginAt); err != nil {
			h.logger.Error("Failed to scan user", "error", err)
			continue
		}

		roles, _ := GetUserRoles(h.db, u.ID)
		permissions, _ := GetUserPermissions(h.db, u.ID)
		groups, _ := GetUserGroups(h.db, u.ID)

		// Ensure arrays are never nil (use empty slice instead)
		if roles == nil {
			roles = []string{}
		}
		if permissions == nil {
			permissions = []string{}
		}
		if groups == nil {
			groups = []string{}
		}

		users = append(users, map[string]interface{}{
			"id":            u.ID,
			"username":      u.Username,
			"email":         u.Email,
			"full_name":     u.FullName,
			"is_active":     u.IsActive,
			"is_system":     u.IsSystem,
			"roles":         roles,
			"permissions":   permissions,
			"groups":        groups,
			"created_at":    u.CreatedAt,
			"updated_at":    u.UpdatedAt,
			"last_login_at": u.LastLoginAt,
		})
	}

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

// GetUser retrieves a single user
// Permission: User can view their own profile, or admin can view any profile
func (h *Handler) GetUser(c *gin.Context) {
	userID := c.Param("id")

	// Get current authenticated user from context
	authUser, exists := c.Get("user")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
		return
	}

	currentUser, ok := authUser.(*User)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"})
		return
	}

	// Check permission: user can view own profile, or admin can view any profile
	canView := false
	if currentUser.ID == userID {
		canView = true
	} else {
		// Check if current user is admin
		roles, err := GetUserRoles(h.db, currentUser.ID)
		if err == nil {
			for _, role := range roles {
				if role == "admin" {
					canView = true
					break
				}
			}
		}
	}

	if !canView {
		c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
		return
	}

	user, err := GetUserByID(h.db, userID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}

	roles, _ := GetUserRoles(h.db, userID)
	permissions, _ := GetUserPermissions(h.db, userID)
	groups, _ := GetUserGroups(h.db, userID)

	// Ensure arrays are never nil (use empty slice instead)
	if roles == nil {
		roles = []string{}
	}
	if permissions == nil {
		permissions = []string{}
	}
	if groups == nil {
		groups = []string{}
	}

	c.JSON(http.StatusOK, gin.H{
		"id":            user.ID,
		"username":      user.Username,
		"email":         user.Email,
		"full_name":     user.FullName,
		"is_active":     user.IsActive,
		"is_system":     user.IsSystem,
		"roles":         roles,
		"permissions":   permissions,
		"groups":        groups,
		"created_at":    user.CreatedAt,
		"updated_at":    user.UpdatedAt,
		"last_login_at": user.LastLoginAt,
	})
}

// CreateUser creates a new user
func (h *Handler) CreateUser(c *gin.Context) {
	var req struct {
		Username string `json:"username" binding:"required"`
		Email    string `json:"email" binding:"required,email"`
		Password string `json:"password" binding:"required"`
		FullName string `json:"full_name"`
	}

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

	// Hash password with Argon2id
	passwordHash, err := password.HashPassword(req.Password, h.config.Auth.Argon2Params)
	if err != nil {
		h.logger.Error("Failed to hash password", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create user"})
		return
	}

	query := `
		INSERT INTO users (username, email, password_hash, full_name)
		VALUES ($1, $2, $3, $4)
		RETURNING id
	`

	var userID string
	err = h.db.QueryRow(query, req.Username, req.Email, passwordHash, req.FullName).Scan(&userID)
	if err != nil {
		h.logger.Error("Failed to create user", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create user"})
		return
	}

	h.logger.Info("User created", "user_id", userID, "username", req.Username)
	c.JSON(http.StatusCreated, gin.H{"id": userID, "username": req.Username})
}

// UpdateUser updates an existing user
func (h *Handler) UpdateUser(c *gin.Context) {
	userID := c.Param("id")

	var req struct {
		Email    *string   `json:"email"`
		FullName *string   `json:"full_name"`
		IsActive *bool     `json:"is_active"`
		Roles    *[]string `json:"roles"`
		Groups   *[]string `json:"groups"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Failed to bind JSON", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
		return
	}

	h.logger.Info("UpdateUser request received", "user_id", userID, "email", req.Email, "full_name", req.FullName, "is_active", req.IsActive, "roles", req.Roles, "groups", req.Groups)

	// Build update query dynamically
	updates := []string{"updated_at = NOW()"}
	args := []interface{}{}
	argPos := 1

	if req.Email != nil {
		updates = append(updates, fmt.Sprintf("email = $%d", argPos))
		args = append(args, *req.Email)
		argPos++
	}
	if req.FullName != nil {
		updates = append(updates, fmt.Sprintf("full_name = $%d", argPos))
		args = append(args, *req.FullName)
		argPos++
	}
	if req.IsActive != nil {
		updates = append(updates, fmt.Sprintf("is_active = $%d", argPos))
		args = append(args, *req.IsActive)
		argPos++
	}

	// Allow update if roles or groups are provided, even if no other fields are updated
	// Note: req.Roles and req.Groups can be empty arrays ([]), which is different from nil
	// Empty array means "remove all roles/groups", nil means "don't change roles/groups"
	if len(updates) == 1 && req.Roles == nil && req.Groups == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

	// Update user basic info if there are any changes
	if len(updates) > 1 {
		args = append(args, userID)
		query := "UPDATE users SET " + strings.Join(updates, ", ") + fmt.Sprintf(" WHERE id = $%d", argPos)
		_, err := h.db.Exec(query, args...)
		if err != nil {
			h.logger.Error("Failed to update user", "error", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update user"})
			return
		}
	}

	// Get current user ID from context for audit
	authUser, _ := c.Get("user")
	currentUser := authUser.(*User)

	// Update roles if provided
	if req.Roles != nil {
		h.logger.Info("Updating user roles", "user_id", userID, "requested_roles", *req.Roles)
		currentRoles, err := GetUserRoles(h.db, userID)
		if err != nil {
			h.logger.Error("Failed to get current roles for user", "user_id", userID, "error", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to process user roles"})
			return
		}
		h.logger.Info("Current user roles", "user_id", userID, "current_roles", currentRoles)

		rolesToAdd := []string{}
		rolesToRemove := []string{}

		// Find roles to add
		for _, newRole := range *req.Roles {
			found := false
			for _, currentRole := range currentRoles {
				if newRole == currentRole {
					found = true
					break
				}
			}
			if !found {
				rolesToAdd = append(rolesToAdd, newRole)
			}
		}

		// Find roles to remove
		for _, currentRole := range currentRoles {
			found := false
			for _, newRole := range *req.Roles {
				if currentRole == newRole {
					found = true
					break
				}
			}
			if !found {
				rolesToRemove = append(rolesToRemove, currentRole)
			}
		}

		h.logger.Info("Roles to add", "user_id", userID, "roles_to_add", rolesToAdd, "count", len(rolesToAdd))
		h.logger.Info("Roles to remove", "user_id", userID, "roles_to_remove", rolesToRemove, "count", len(rolesToRemove))

		// Add new roles
		if len(rolesToAdd) == 0 {
			h.logger.Info("No roles to add", "user_id", userID)
		}
		for _, roleName := range rolesToAdd {
			h.logger.Info("Processing role to add", "user_id", userID, "role_name", roleName)
			roleID, err := GetRoleIDByName(h.db, roleName)
			if err != nil {
				if err == sql.ErrNoRows {
					h.logger.Warn("Attempted to add non-existent role to user", "user_id", userID, "role_name", roleName)
					c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("role '%s' not found", roleName)})
					return
				}
				h.logger.Error("Failed to get role ID by name", "role_name", roleName, "error", err)
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to process roles"})
				return
			}
			h.logger.Info("Attempting to add role", "user_id", userID, "role_id", roleID, "role_name", roleName, "assigned_by", currentUser.ID)
			if err := AddUserRole(h.db, userID, roleID, currentUser.ID); err != nil {
				h.logger.Error("Failed to add role to user", "user_id", userID, "role_id", roleID, "role_name", roleName, "error", err)
				c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to add role '%s': %v", roleName, err)})
				return
			}
			h.logger.Info("Role successfully added to user", "user_id", userID, "role_id", roleID, "role_name", roleName)
		}

		// Remove old roles
		for _, roleName := range rolesToRemove {
			roleID, err := GetRoleIDByName(h.db, roleName)
			if err != nil {
				// This case should be rare, but handle it defensively
				h.logger.Error("Failed to get role ID for role to be removed", "role_name", roleName, "error", err)
				continue
			}
			if err := RemoveUserRole(h.db, userID, roleID); err != nil {
				h.logger.Error("Failed to remove role from user", "user_id", userID, "role_id", roleID, "error", err)
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove role"})
				return
			}
			h.logger.Info("Role removed from user", "user_id", userID, "role_name", roleName)
		}
	}

	// Update groups if provided
	if req.Groups != nil {
		h.logger.Info("Updating user groups", "user_id", userID, "groups", *req.Groups)
		currentGroups, err := GetUserGroups(h.db, userID)
		if err != nil {
			h.logger.Error("Failed to get current groups for user", "user_id", userID, "error", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to process user groups"})
			return
		}

		groupsToAdd := []string{}
		groupsToRemove := []string{}

		// Find groups to add
		for _, newGroup := range *req.Groups {
			found := false
			for _, currentGroup := range currentGroups {
				if newGroup == currentGroup {
					found = true
					break
				}
			}
			if !found {
				groupsToAdd = append(groupsToAdd, newGroup)
			}
		}

		// Find groups to remove
		for _, currentGroup := range currentGroups {
			found := false
			for _, newGroup := range *req.Groups {
				if currentGroup == newGroup {
					found = true
					break
				}
			}
			if !found {
				groupsToRemove = append(groupsToRemove, currentGroup)
			}
		}

		// Add new groups
		for _, groupName := range groupsToAdd {
			group, err := GetGroupByName(h.db, groupName)
			if err != nil {
				if err == sql.ErrNoRows {
					h.logger.Warn("Attempted to add user to non-existent group", "user_id", userID, "group_name", groupName)
					c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("group '%s' not found", groupName)})
					return
				}
				h.logger.Error("Failed to get group by name", "group_name", groupName, "error", err)
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to process groups"})
				return
			}
			if err := AddUserToGroup(h.db, userID, group.ID, currentUser.ID); err != nil {
				h.logger.Error("Failed to add user to group", "user_id", userID, "group_id", group.ID, "error", err)
				// Don't return early, continue with other groups
				continue
			}
			h.logger.Info("User added to group", "user_id", userID, "group_name", groupName)
		}

		// Remove old groups
		for _, groupName := range groupsToRemove {
			group, err := GetGroupByName(h.db, groupName)
			if err != nil {
				// This case should be rare, but handle it defensively
				h.logger.Error("Failed to get group ID for group to be removed", "group_name", groupName, "error", err)
				continue
			}
			if err := RemoveUserFromGroup(h.db, userID, group.ID); err != nil {
				h.logger.Error("Failed to remove user from group", "user_id", userID, "group_id", group.ID, "error", err)
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove user from group"})
				return
			}
			h.logger.Info("User removed from group", "user_id", userID, "group_name", groupName)
		}
	}

	// Fetch updated user data to return
	updatedUser, err := GetUserByID(h.db, userID)
	if err != nil {
		h.logger.Error("Failed to fetch updated user", "user_id", userID, "error", err)
		c.JSON(http.StatusOK, gin.H{"message": "user updated successfully"})
		return
	}

	// Get updated roles, permissions, and groups
	updatedRoles, _ := GetUserRoles(h.db, userID)
	updatedPermissions, _ := GetUserPermissions(h.db, userID)
	updatedGroups, _ := GetUserGroups(h.db, userID)

	// Ensure arrays are never nil
	if updatedRoles == nil {
		updatedRoles = []string{}
	}
	if updatedPermissions == nil {
		updatedPermissions = []string{}
	}
	if updatedGroups == nil {
		updatedGroups = []string{}
	}

	h.logger.Info("User updated", "user_id", userID, "roles", updatedRoles, "groups", updatedGroups)
	c.JSON(http.StatusOK, gin.H{
		"message": "user updated successfully",
		"user": gin.H{
			"id":            updatedUser.ID,
			"username":      updatedUser.Username,
			"email":         updatedUser.Email,
			"full_name":     updatedUser.FullName,
			"is_active":     updatedUser.IsActive,
			"is_system":     updatedUser.IsSystem,
			"roles":         updatedRoles,
			"permissions":   updatedPermissions,
			"groups":        updatedGroups,
			"created_at":    updatedUser.CreatedAt,
			"updated_at":    updatedUser.UpdatedAt,
			"last_login_at": updatedUser.LastLoginAt,
		},
	})
}

// DeleteUser deletes a user
func (h *Handler) DeleteUser(c *gin.Context) {
	userID := c.Param("id")

	// Check if user is system user
	var isSystem bool
	err := h.db.QueryRow("SELECT is_system FROM users WHERE id = $1", userID).Scan(&isSystem)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}

	if isSystem {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete system user"})
		return
	}

	_, err = h.db.Exec("DELETE FROM users WHERE id = $1", userID)
	if err != nil {
		h.logger.Error("Failed to delete user", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete user"})
		return
	}

	h.logger.Info("User deleted", "user_id", userID)
	c.JSON(http.StatusOK, gin.H{"message": "user deleted successfully"})
}

// ListGroups lists all groups
func (h *Handler) ListGroups(c *gin.Context) {
	query := `
		SELECT g.id, g.name, g.description, g.is_system, g.created_at, g.updated_at,
		       COUNT(DISTINCT ug.user_id) as user_count,
		       COUNT(DISTINCT gr.role_id) as role_count
		FROM groups g
		LEFT JOIN user_groups ug ON g.id = ug.group_id
		LEFT JOIN group_roles gr ON g.id = gr.group_id
		GROUP BY g.id, g.name, g.description, g.is_system, g.created_at, g.updated_at
		ORDER BY g.name
	`

	rows, err := h.db.Query(query)
	if err != nil {
		h.logger.Error("Failed to list groups", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list groups"})
		return
	}
	defer rows.Close()

	var groups []map[string]interface{}
	for rows.Next() {
		var g struct {
			ID          string
			Name        string
			Description sql.NullString
			IsSystem    bool
			CreatedAt   string
			UpdatedAt   string
			UserCount   int
			RoleCount   int
		}
		if err := rows.Scan(&g.ID, &g.Name, &g.Description, &g.IsSystem,
			&g.CreatedAt, &g.UpdatedAt, &g.UserCount, &g.RoleCount); err != nil {
			h.logger.Error("Failed to scan group", "error", err)
			continue
		}

		groups = append(groups, map[string]interface{}{
			"id":          g.ID,
			"name":        g.Name,
			"description": g.Description.String,
			"is_system":   g.IsSystem,
			"user_count":  g.UserCount,
			"role_count":  g.RoleCount,
			"created_at":  g.CreatedAt,
			"updated_at":  g.UpdatedAt,
		})
	}

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

// GetGroup retrieves a single group
func (h *Handler) GetGroup(c *gin.Context) {
	groupID := c.Param("id")

	group, err := GetGroupByID(h.db, groupID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
		return
	}

	users, _ := GetGroupUsers(h.db, groupID)
	roles, _ := GetGroupRoles(h.db, groupID)

	c.JSON(http.StatusOK, gin.H{
		"id":          group.ID,
		"name":        group.Name,
		"description": group.Description,
		"is_system":   group.IsSystem,
		"user_count":  group.UserCount,
		"role_count":  group.RoleCount,
		"users":       users,
		"roles":       roles,
		"created_at":  group.CreatedAt,
		"updated_at":  group.UpdatedAt,
	})
}

// CreateGroup creates a new group
func (h *Handler) CreateGroup(c *gin.Context) {
	var req struct {
		Name        string `json:"name" binding:"required"`
		Description string `json:"description"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid request to create group", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	// Trim whitespace
	req.Name = strings.TrimSpace(req.Name)
	if req.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
		return
	}

	// Handle empty description
	description := strings.TrimSpace(req.Description)
	if description == "" {
		description = ""
	}

	query := `
		INSERT INTO groups (name, description)
		VALUES ($1, $2)
		RETURNING id
	`

	var groupID string
	err := h.db.QueryRow(query, req.Name, description).Scan(&groupID)
	if err != nil {
		// Check if it's a unique constraint violation
		if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "unique constraint") {
			h.logger.Error("Group name already exists", "name", req.Name, "error", err)
			c.JSON(http.StatusConflict, gin.H{"error": "group name already exists"})
			return
		}
		h.logger.Error("Failed to create group", "error", err, "name", req.Name)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create group: " + err.Error()})
		return
	}

	h.logger.Info("Group created successfully", "group_id", groupID, "name", req.Name)
	c.JSON(http.StatusCreated, gin.H{"id": groupID, "name": req.Name})
}

// UpdateGroup updates an existing group
func (h *Handler) UpdateGroup(c *gin.Context) {
	groupID := c.Param("id")

	// Check if group is system group
	var isSystem bool
	err := h.db.QueryRow("SELECT is_system FROM groups WHERE id = $1", groupID).Scan(&isSystem)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
		return
	}

	var req struct {
		Name        string `json:"name"`
		Description string `json:"description"`
	}

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

	// Build update query dynamically
	var updates []string
	var args []interface{}
	argIndex := 1

	if req.Name != "" {
		updates = append(updates, fmt.Sprintf("name = $%d", argIndex))
		args = append(args, req.Name)
		argIndex++
	}

	if req.Description != "" {
		updates = append(updates, fmt.Sprintf("description = $%d", argIndex))
		args = append(args, req.Description)
		argIndex++
	}

	if len(updates) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

	updates = append(updates, "updated_at = NOW()")
	args = append(args, groupID)

	query := fmt.Sprintf("UPDATE groups SET %s WHERE id = $%d", strings.Join(updates, ", "), argIndex)

	_, err = h.db.Exec(query, args...)
	if err != nil {
		h.logger.Error("Failed to update group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update group"})
		return
	}

	h.logger.Info("Group updated", "group_id", groupID)
	c.JSON(http.StatusOK, gin.H{"message": "group updated successfully"})
}

// DeleteGroup deletes a group
func (h *Handler) DeleteGroup(c *gin.Context) {
	groupID := c.Param("id")

	// Check if group is system group
	var isSystem bool
	err := h.db.QueryRow("SELECT is_system FROM groups WHERE id = $1", groupID).Scan(&isSystem)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
		return
	}

	if isSystem {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete system group"})
		return
	}

	_, err = h.db.Exec("DELETE FROM groups WHERE id = $1", groupID)
	if err != nil {
		h.logger.Error("Failed to delete group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete group"})
		return
	}

	h.logger.Info("Group deleted", "group_id", groupID)
	c.JSON(http.StatusOK, gin.H{"message": "group deleted successfully"})
}

// AddUserToGroup adds a user to a group
func (h *Handler) AddUserToGroup(c *gin.Context) {
	groupID := c.Param("id")
	var req struct {
		UserID string `json:"user_id" binding:"required"`
	}

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

	// Get current user ID from context
	authUser, _ := c.Get("user")
	currentUser := authUser.(*User)

	err := AddUserToGroup(h.db, req.UserID, groupID, currentUser.ID)
	if err != nil {
		h.logger.Error("Failed to add user to group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to add user to group"})
		return
	}

	h.logger.Info("User added to group", "user_id", req.UserID, "group_id", groupID)
	c.JSON(http.StatusOK, gin.H{"message": "user added to group successfully"})
}

// RemoveUserFromGroup removes a user from a group
func (h *Handler) RemoveUserFromGroup(c *gin.Context) {
	groupID := c.Param("id")
	userID := c.Param("user_id")

	err := RemoveUserFromGroup(h.db, userID, groupID)
	if err != nil {
		h.logger.Error("Failed to remove user from group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove user from group"})
		return
	}

	h.logger.Info("User removed from group", "user_id", userID, "group_id", groupID)
	c.JSON(http.StatusOK, gin.H{"message": "user removed from group successfully"})
}

// AssignRoleToUser assigns a role to a user
func (h *Handler) AssignRoleToUser(c *gin.Context) {
	userID := c.Param("id")
	var req struct {
		RoleName string `json:"role_name" binding:"required"`
	}

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

	// Get role ID by name
	roleID, err := GetRoleIDByName(h.db, req.RoleName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
			return
		}
		h.logger.Error("Failed to get role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign role"})
		return
	}

	// Get current user ID from context
	authUser, _ := c.Get("user")
	currentUser := authUser.(*User)

	err = AddUserRole(h.db, userID, roleID, currentUser.ID)
	if err != nil {
		h.logger.Error("Failed to assign role to user", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign role"})
		return
	}

	h.logger.Info("Role assigned to user", "user_id", userID, "role", req.RoleName)
	c.JSON(http.StatusOK, gin.H{"message": "role assigned successfully"})
}

// RemoveRoleFromUser removes a role from a user
func (h *Handler) RemoveRoleFromUser(c *gin.Context) {
	userID := c.Param("id")

	// Get role_name from query parameter
	roleName := c.Query("role_name")
	if roleName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "role_name is required"})
		return
	}

	// Get role ID by name
	roleID, err := GetRoleIDByName(h.db, roleName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
			return
		}
		h.logger.Error("Failed to get role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove role"})
		return
	}

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

	h.logger.Info("Role removed from user", "user_id", userID, "role", roleName)
	c.JSON(http.StatusOK, gin.H{"message": "role removed successfully"})
}

// AssignGroupToUser assigns a group to a user
func (h *Handler) AssignGroupToUser(c *gin.Context) {
	userID := c.Param("id")
	var req struct {
		GroupName string `json:"group_name" binding:"required"`
	}

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

	// Get group ID by name
	group, err := GetGroupByName(h.db, req.GroupName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
			return
		}
		h.logger.Error("Failed to get group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign group"})
		return
	}
	groupID := group.ID

	// Get current user ID from context
	authUser, _ := c.Get("user")
	currentUser := authUser.(*User)

	err = AddUserToGroup(h.db, userID, groupID, currentUser.ID)
	if err != nil {
		h.logger.Error("Failed to assign group to user", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign group"})
		return
	}

	h.logger.Info("Group assigned to user", "user_id", userID, "group", req.GroupName)
	c.JSON(http.StatusOK, gin.H{"message": "group assigned successfully"})
}

// RemoveGroupFromUser removes a group from a user
func (h *Handler) RemoveGroupFromUser(c *gin.Context) {
	userID := c.Param("id")

	// Get group_name from query parameter
	groupName := c.Query("group_name")
	if groupName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "group_name is required"})
		return
	}

	// Get group ID by name
	group, err := GetGroupByName(h.db, groupName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
			return
		}
		h.logger.Error("Failed to get group", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove group"})
		return
	}
	groupID := group.ID

	err = RemoveUserFromGroup(h.db, userID, groupID)
	if err != nil {
		h.logger.Error("Failed to remove group from user", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove group"})
		return
	}

	h.logger.Info("Group removed from user", "user_id", userID, "group", groupName)
	c.JSON(http.StatusOK, gin.H{"message": "group removed successfully"})
}

// ListRoles lists all available roles
func (h *Handler) ListRoles(c *gin.Context) {
	roles, err := ListRoles(h.db)
	if err != nil {
		h.logger.Error("Failed to list roles", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list roles"})
		return
	}

	var result []map[string]interface{}
	for _, role := range roles {
		// Get user count for this role
		userCount := 0
		userIDs, _ := GetRoleUsers(h.db, role.ID)
		userCount = len(userIDs)

		result = append(result, map[string]interface{}{
			"id":          role.ID,
			"name":        role.Name,
			"description": role.Description,
			"is_system":   role.IsSystem,
			"user_count":  userCount,
			"created_at":  role.CreatedAt,
			"updated_at":  role.UpdatedAt,
		})
	}

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

// GetRole retrieves a single role
func (h *Handler) GetRole(c *gin.Context) {
	roleID := c.Param("id")

	role, err := GetRoleByID(h.db, roleID)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
			return
		}
		h.logger.Error("Failed to get role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get role"})
		return
	}

	// Get user count
	userIDs, _ := GetRoleUsers(h.db, role.ID)

	c.JSON(http.StatusOK, gin.H{
		"id":          role.ID,
		"name":        role.Name,
		"description": role.Description,
		"is_system":   role.IsSystem,
		"user_count":  len(userIDs),
		"created_at":  role.CreatedAt,
		"updated_at":  role.UpdatedAt,
	})
}

// CreateRole creates a new role
func (h *Handler) CreateRole(c *gin.Context) {
	var req struct {
		Name        string `json:"name" binding:"required"`
		Description string `json:"description"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid request to create role", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	// Trim whitespace
	req.Name = strings.TrimSpace(req.Name)
	if req.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
		return
	}

	// Handle empty description
	description := strings.TrimSpace(req.Description)
	if description == "" {
		description = ""
	}

	role, err := CreateRole(h.db, req.Name, description)
	if err != nil {
		// Check if it's a unique constraint violation
		if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "unique constraint") {
			h.logger.Error("Role name already exists", "name", req.Name, "error", err)
			c.JSON(http.StatusConflict, gin.H{"error": "role name already exists"})
			return
		}
		h.logger.Error("Failed to create role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create role"})
		return
	}

	h.logger.Info("Role created", "role_id", role.ID, "name", role.Name)
	c.JSON(http.StatusCreated, gin.H{"id": role.ID, "name": role.Name})
}

// UpdateRole updates an existing role
func (h *Handler) UpdateRole(c *gin.Context) {
	roleID := c.Param("id")

	// Check if role is system role
	var isSystem bool
	err := h.db.QueryRow("SELECT is_system FROM roles WHERE id = $1", roleID).Scan(&isSystem)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
		return
	}

	if isSystem {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot modify system role"})
		return
	}

	var req struct {
		Name        *string `json:"name"`
		Description *string `json:"description"`
	}

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

	// Build update query dynamically
	updates := []string{"updated_at = NOW()"}
	args := []interface{}{}
	argIndex := 1

	if req.Name != nil {
		name := strings.TrimSpace(*req.Name)
		if name == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "name cannot be empty"})
			return
		}
		updates = append(updates, fmt.Sprintf("name = $%d", argIndex))
		args = append(args, name)
		argIndex++
	}

	if req.Description != nil {
		description := strings.TrimSpace(*req.Description)
		updates = append(updates, fmt.Sprintf("description = $%d", argIndex))
		args = append(args, description)
		argIndex++
	}

	if len(updates) == 1 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

	args = append(args, roleID)
	query := "UPDATE roles SET " + strings.Join(updates, ", ") + fmt.Sprintf(" WHERE id = $%d", argIndex)

	_, err = h.db.Exec(query, args...)
	if err != nil {
		// Check if it's a unique constraint violation
		if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "unique constraint") {
			h.logger.Error("Role name already exists", "error", err)
			c.JSON(http.StatusConflict, gin.H{"error": "role name already exists"})
			return
		}
		h.logger.Error("Failed to update role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update role"})
		return
	}

	h.logger.Info("Role updated", "role_id", roleID)
	c.JSON(http.StatusOK, gin.H{"message": "role updated successfully"})
}

// DeleteRole deletes a role
func (h *Handler) DeleteRole(c *gin.Context) {
	roleID := c.Param("id")

	// Check if role is system role
	var isSystem bool
	err := h.db.QueryRow("SELECT is_system FROM roles WHERE id = $1", roleID).Scan(&isSystem)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
		return
	}

	if isSystem {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete system role"})
		return
	}

	_, err = h.db.Exec("DELETE FROM roles WHERE id = $1", roleID)
	if err != nil {
		h.logger.Error("Failed to delete role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete role"})
		return
	}

	h.logger.Info("Role deleted", "role_id", roleID)
	c.JSON(http.StatusOK, gin.H{"message": "role deleted successfully"})
}

// GetRolePermissions retrieves all permissions for a role
func (h *Handler) GetRolePermissions(c *gin.Context) {
	roleID := c.Param("id")

	permissions, err := GetRolePermissions(h.db, roleID)
	if err != nil {
		h.logger.Error("Failed to get role permissions", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get role permissions"})
		return
	}

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

// AssignPermissionToRole assigns a permission to a role
func (h *Handler) AssignPermissionToRole(c *gin.Context) {
	roleID := c.Param("id")
	var req struct {
		PermissionName string `json:"permission_name" binding:"required"`
	}

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

	// Get permission ID by name
	permissionID, err := GetPermissionIDByName(h.db, req.PermissionName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "permission not found"})
			return
		}
		h.logger.Error("Failed to get permission", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign permission"})
		return
	}

	err = AddPermissionToRole(h.db, roleID, permissionID)
	if err != nil {
		h.logger.Error("Failed to assign permission to role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign permission"})
		return
	}

	h.logger.Info("Permission assigned to role", "role_id", roleID, "permission", req.PermissionName)
	c.JSON(http.StatusOK, gin.H{"message": "permission assigned successfully"})
}

// RemovePermissionFromRole removes a permission from a role
func (h *Handler) RemovePermissionFromRole(c *gin.Context) {
	roleID := c.Param("id")

	// For DELETE requests, we can get permission_name from query param or body
	var req struct {
		PermissionName string `json:"permission_name"`
	}

	// Try to get from query param first
	permissionName := c.Query("permission_name")
	if permissionName == "" {
		// Try to get from body
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "permission_name is required"})
			return
		}
		permissionName = req.PermissionName
	}

	if permissionName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "permission_name is required"})
		return
	}

	// Get permission ID by name
	permissionID, err := GetPermissionIDByName(h.db, permissionName)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "permission not found"})
			return
		}
		h.logger.Error("Failed to get permission", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove permission"})
		return
	}

	err = RemovePermissionFromRole(h.db, roleID, permissionID)
	if err != nil {
		h.logger.Error("Failed to remove permission from role", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove permission"})
		return
	}

	h.logger.Info("Permission removed from role", "role_id", roleID, "permission", permissionName)
	c.JSON(http.StatusOK, gin.H{"message": "permission removed successfully"})
}

// ListPermissions lists all available permissions
func (h *Handler) ListPermissions(c *gin.Context) {
	permissions, err := ListPermissions(h.db)
	if err != nil {
		h.logger.Error("Failed to list permissions", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list permissions"})
		return
	}

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