// veza-backend-api/internal/api/user/handler.go
package user

import (
	"context"
	"net/http"
	"strconv"
	"time"

	"veza-backend-api/internal/common"
	"veza-backend-api/internal/response"
	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid" // Added import
)

// UserServiceInterface defines the interface for user operations
// This allows for easier testing with mocks
type UserServiceInterface interface {
	GetUserByID(userID uuid.UUID) (*UserResponse, error)
	GetUsers(page, limit int, search string) ([]UserResponse, int, error)
	UpdateUser(userID uuid.UUID, req UpdateUserRequest) (*UserResponse, error)
	ChangePassword(userID uuid.UUID, currentPassword, newPassword string) error
	GetUserPreferences(userID uuid.UUID) (*UserPreferencesResponse, error)
	UpdateUserPreferences(userID uuid.UUID, req UserPreferencesRequest) (*UserPreferencesResponse, error)
	DeleteAccount(userID uuid.UUID, password, reason string) error
	RecoverAccount(email, password string) error
	RequestDataDeletion(userID uuid.UUID, password, reason string) error
	GetAccountStatus(userID uuid.UUID) (*AccountStatus, error)
}

// DataExportServiceInterface defines the interface for data export operations
type DataExportServiceInterface interface {
	ExportUserDataAsJSON(ctx context.Context, userID uuid.UUID) ([]byte, error)
}

type Handler struct {
	service           UserServiceInterface
	dataExportService DataExportServiceInterface // BE-SVC-022: Service d'export de données
}

func NewHandler(service *Service, dataExportService *services.DataExportService) *Handler {
	return &Handler{
		service:           service,
		dataExportService: dataExportService,
	}
}

// NewHandlerWithInterfaces creates a new handler with interfaces (for testing)
func NewHandlerWithInterfaces(service UserServiceInterface, dataExportService DataExportServiceInterface) *Handler {
	return &Handler{
		service:           service,
		dataExportService: dataExportService,
	}
}

// GetMe récupère le profil de l'utilisateur connecté
func (h *Handler) GetMe(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	user, err := h.service.GetUserByID(userID)
	if err != nil {
		response.NotFound(c, "User not found")
		return
	}

	response.Success(c, user)
}

// UpdateMe met à jour le profil de l'utilisateur connecté
func (h *Handler) UpdateMe(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	var req UpdateUserRequest
	if !common.BindAndValidate(c, &req) {
		return
	}

	user, err := h.service.UpdateUser(userID, req)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, user)
}

// ChangePassword change le mot de passe de l'utilisateur
func (h *Handler) ChangePassword(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	var req struct {
		CurrentPassword string `json:"current_password" binding:"required"`
		NewPassword     string `json:"new_password" binding:"required,min=8"`
	}

	if !common.BindAndValidate(c, &req) {
		return
	}

	err := h.service.ChangePassword(userID, req.CurrentPassword, req.NewPassword)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, nil)
}

// GetUsers liste tous les utilisateurs
func (h *Handler) GetUsers(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
	search := c.Query("search")

	users, total, err := h.service.GetUsers(page, limit, search)
	if err != nil {
		response.InternalServerError(c, "Failed to retrieve users")
		return
	}

	response.Success(c, gin.H{
		"data": users,
		"pagination": gin.H{
			"page":        page,
			"limit":       limit,
			"total":       total,
			"total_pages": (total + limit - 1) / limit,
		},
	})
}

// GetUsersExceptMe liste tous les utilisateurs sauf l'utilisateur connecté
func (h *Handler) GetUsersExceptMe(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
	search := c.Query("search")

	// Ajouter le filtre pour exclure l'utilisateur actuel
	users, total, err := h.service.GetUsers(page, limit, search)
	if err != nil {
		response.InternalServerError(c, "Failed to retrieve users")
		return
	}

	// Filtrer l'utilisateur connecté
	filteredUsers := []UserResponse{}
	for _, user := range users {
		if user.ID != userID { // Direct comparison of uuid.UUID
			filteredUsers = append(filteredUsers, user)
		}
	}

	response.Success(c, gin.H{
		"data": filteredUsers,
		"pagination": gin.H{
			"page":        page,
			"limit":       limit,
			"total":       total - 1, // -1 car on exclut l'utilisateur connecté
			"total_pages": (total + limit - 2) / limit,
		},
	})
}

// SearchUsers recherche des utilisateurs
func (h *Handler) SearchUsers(c *gin.Context) {
	query := c.Query("q")
	if query == "" {
		response.BadRequest(c, "Query parameter 'q' is required")
		return
	}

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))

	users, total, err := h.service.GetUsers(page, limit, query)
	if err != nil {
		response.InternalServerError(c, "Failed to search users")
		return
	}

	response.Success(c, gin.H{
		"data": users,
		"pagination": gin.H{
			"page":        page,
			"limit":       limit,
			"total":       total,
			"total_pages": (total + limit - 1) / limit,
		},
	})
}

func (h *Handler) GetUserAvatar(c *gin.Context) {
	idStr := c.Param("id")
	userID, err := uuid.Parse(idStr)
	if err != nil {
		response.BadRequest(c, "Invalid user ID")
		return
	}

	user, err := h.service.GetUserByID(userID)
	if err != nil {
		response.NotFound(c, "User not found")
		return
	}

	// ✅ Correct way to handle sql.NullString
	if !user.Avatar.Valid || user.Avatar.String == "" {
		response.NotFound(c, "No avatar found")
		return
	}

	// Rediriger vers l'URL de l'avatar ou servir le fichier
	c.Redirect(http.StatusFound, user.Avatar.String)
}

// GetPreferences récupère les préférences de l'utilisateur connecté
func (h *Handler) GetPreferences(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	preferences, err := h.service.GetUserPreferences(userID)
	if err != nil {
		response.InternalServerError(c, "Failed to get preferences")
		return
	}

	response.Success(c, preferences)
}

// UpdatePreferences met à jour les préférences de l'utilisateur connecté
func (h *Handler) UpdatePreferences(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	var req UserPreferencesRequest
	if !common.BindAndValidate(c, &req) {
		return
	}

	preferences, err := h.service.UpdateUserPreferences(userID, req)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, preferences)
}

// DeleteAccount supprime le compte de l'utilisateur (soft delete)
func (h *Handler) DeleteAccount(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	var req struct {
		Password    string `json:"password" binding:"required"`
		Reason      string `json:"reason"`
		ConfirmText string `json:"confirm_text" binding:"required"`
	}

	if !common.BindAndValidate(c, &req) {
		return
	}

	// Vérifier le texte de confirmation
	if req.ConfirmText != "DELETE" {
		response.BadRequest(c, "Confirmation text must be 'DELETE'")
		return
	}

	err := h.service.DeleteAccount(userID, req.Password, req.Reason)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, nil)
}

// RecoverAccount récupère un compte supprimé
func (h *Handler) RecoverAccount(c *gin.Context) {
	var req struct {
		Email    string `json:"email" binding:"required,email"`
		Password string `json:"password" binding:"required"`
	}

	if !common.BindAndValidate(c, &req) {
		return
	}

	err := h.service.RecoverAccount(req.Email, req.Password)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, nil)
}

// ExportData exporte les données de l'utilisateur (RGPD) - BE-SVC-022
func (h *Handler) ExportData(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	// Utiliser le nouveau service d'export de données
	if h.dataExportService == nil {
		response.InternalServerError(c, "Data export service not available")
		return
	}

	// Exporter les données au format JSON
	jsonData, err := h.dataExportService.ExportUserDataAsJSON(c.Request.Context(), userID)
	if err != nil {
		response.InternalServerError(c, "Failed to export user data: "+err.Error())
		return
	}

	// Définir les headers pour le téléchargement
	filename := "veza-data-export-" + time.Now().Format("2006-01-02T15-04-05") + ".json"
	c.Header("Content-Type", "application/json")
	c.Header("Content-Disposition", `attachment; filename="`+filename+`"`)
	c.Header("Content-Length", strconv.Itoa(len(jsonData)))

	// Envoyer le fichier JSON
	c.Data(http.StatusOK, "application/json", jsonData)
}

// RequestDataDeletion demande la suppression définitive des données (RGPD)
func (h *Handler) RequestDataDeletion(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	var req struct {
		Password string `json:"password" binding:"required"`
		Reason   string `json:"reason"`
	}

	if !common.BindAndValidate(c, &req) {
		return
	}

	err := h.service.RequestDataDeletion(userID, req.Password, req.Reason)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}

	response.Success(c, nil)
}

// GetAccountStatus récupère le statut du compte
func (h *Handler) GetAccountStatus(c *gin.Context) {
	userID, exists := common.GetUserIDFromContext(c)
	if !exists {
		response.Unauthorized(c, "User ID not found")
		return
	}

	status, err := h.service.GetAccountStatus(userID)
	if err != nil {
		response.InternalServerError(c, "Failed to get account status")
		return
	}

	response.Success(c, status)
}
