package iam

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

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

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

// NewHandler creates a new IAM handler
func NewHandler(db *database.DB, log *logger.Logger) *Handler {
	return &Handler{
		db:     db,
		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
		}

		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,
			"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
func (h *Handler) GetUser(c *gin.Context) {
	userID := c.Param("id")

	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)

	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,
		"created_at":  user.CreatedAt,
		"updated_at":  user.UpdatedAt,
	})
}

// 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
	}

	// TODO: Hash password with Argon2id
	passwordHash := req.Password // Placeholder

	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"`
	}

	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{}{}
	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++
	}

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

	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
	}

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

// 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"})
}

