package handlers

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

	apperrors "veza-backend-api/internal/errors"
	"veza-backend-api/internal/services"

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

// RoomServiceInterface defines the interface for room service operations
type RoomServiceInterface interface {
	CreateRoom(ctx context.Context, userID uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error)
	GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error)
	GetRoom(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error)
	UpdateRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req services.UpdateRoomRequest) (*services.RoomResponse, error) // BE-API-012: Update room method
	AddMember(ctx context.Context, roomID, userID uuid.UUID) error
	RemoveMember(ctx context.Context, roomID, userID uuid.UUID) error // BE-API-011: Remove member method
	GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error)
	DeleteRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error // BE-API-010: Delete room method
}

// RoomHandler gère les opérations sur les rooms (conversations)
type RoomHandler struct {
	roomService   RoomServiceInterface
	logger        *zap.Logger
	commonHandler *CommonHandler
}

// NewRoomHandler crée une nouvelle instance de RoomHandler
func NewRoomHandler(roomService RoomServiceInterface, logger *zap.Logger) *RoomHandler {
	return &RoomHandler{
		roomService:   roomService,
		logger:        logger,
		commonHandler: NewCommonHandler(logger),
	}
}

// CreateRoom gère la création d'une nouvelle room
// POST /api/v1/conversations
func (h *RoomHandler) CreateRoom(c *gin.Context) {
	// Récupérer l'ID utilisateur du contexte
	userIDInterface, exists := c.Get("user_id")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	// Convertir userID en uuid.UUID
	userID, ok := userIDInterface.(uuid.UUID)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type in context"})
		return
	}

	// Parser la requête
	var req services.CreateRoomRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Valider le type de room si non spécifié
	if req.Type == "" {
		req.Type = "public"
	}

	// Créer la room
	room, err := h.roomService.CreateRoom(c.Request.Context(), userID, req)
	if err != nil {
		h.logger.Error("failed to create room",
			zap.Error(err),
			zap.String("user_id", userID.String()),
			zap.String("room_name", req.Name))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create conversation"})
		return
	}

	h.logger.Info("room created successfully",
		zap.String("room_id", room.ID.String()),
		zap.String("user_id", userID.String()),
		zap.String("room_name", req.Name))

	RespondSuccess(c, http.StatusCreated, room)
}

// GetUserRooms récupère toutes les rooms d'un utilisateur
// GET /api/v1/conversations
func (h *RoomHandler) GetUserRooms(c *gin.Context) {
	// Récupérer l'ID utilisateur du contexte
	userIDInterface, exists := c.Get("user_id")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	// Convertir userID en uuid.UUID
	userID, ok := userIDInterface.(uuid.UUID)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type in context"})
		return
	}

	// Récupérer les rooms
	rooms, err := h.roomService.GetUserRooms(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("failed to get user rooms",
			zap.Error(err),
			zap.String("user_id", userID.String()))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch conversations"})
		return
	}

	RespondSuccess(c, http.StatusOK, gin.H{
		"conversations": rooms,
		"total":         len(rooms),
	})
}

// GetRoom récupère une room par son ID
// GET /api/v1/conversations/:id
func (h *RoomHandler) GetRoom(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
		return
	}

	// Récupérer la room
	room, err := h.roomService.GetRoom(c.Request.Context(), roomID)
	if err != nil {
		if errors.Is(err, services.ErrRoomNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
			return
		}
		h.logger.Error("failed to get room",
			zap.Error(err),
			zap.String("room_id", roomID.String()))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get conversation"})
		return
	}

	RespondSuccess(c, http.StatusOK, room)
}

// UpdateRoom met à jour une room (conversation)
// PUT /api/v1/conversations/:id
// BE-API-012: Implement conversation update endpoint
func (h *RoomHandler) UpdateRoom(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
		return
	}

	// Récupérer l'ID utilisateur du contexte
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Erreur déjà envoyée par GetUserIDUUID
	}

	// Parser la requête
	var req services.UpdateRoomRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Mettre à jour la room
	room, err := h.roomService.UpdateRoom(c.Request.Context(), roomID, userID, req)
	if err != nil {
		if err.Error() == "room not found" || errors.Is(err, services.ErrRoomNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
			return
		}
		if err.Error() == "forbidden: only room creator can update the room" {
			RespondWithAppError(c, apperrors.NewForbiddenError("only room creator can update the room"))
			return
		}
		h.logger.Error("failed to update room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to update conversation", err))
		return
	}

	h.logger.Info("room updated successfully",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	RespondSuccess(c, http.StatusOK, room)
}

// AddMemberRequest représente une requête pour ajouter un membre à une room
// MOD-P1-001: Ajout tags validate pour validation systématique
type AddMemberRequest struct {
	UserID uuid.UUID `json:"user_id" binding:"required" validate:"required"` // Changed to UUID
}

// AddMember ajoute un membre à une room
// POST /api/v1/conversations/:id/members
func (h *RoomHandler) AddMember(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
		return
	}

	// Parser la requête
	var req AddMemberRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Ajouter le membre
	if err := h.roomService.AddMember(c.Request.Context(), roomID, req.UserID); err != nil {
		h.logger.Error("failed to add member to room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", req.UserID.String()))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add member"})
		return
	}

	h.logger.Info("member added to room",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", req.UserID.String()))

	RespondSuccess(c, http.StatusOK, gin.H{"message": "Member added successfully"})
}

// GetRoomHistory récupère l'historique des messages d'une room
// GET /api/v1/conversations/:id/history
func (h *RoomHandler) GetRoomHistory(c *gin.Context) {
	conversationIDStr := c.Param("id")
	conversationID, err := uuid.Parse(conversationIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
		return
	}

	limit := c.DefaultQuery("limit", "50")
	offset := c.DefaultQuery("offset", "0")

	limitInt, err := strconv.Atoi(limit)
	if err != nil || limitInt <= 0 {
		limitInt = 50
	}
	offsetInt, err := strconv.Atoi(offset)
	if err != nil || offsetInt < 0 {
		offsetInt = 0
	}

	messages, err := h.roomService.GetRoomHistory(c.Request.Context(), conversationID, limitInt, offsetInt)
	if err != nil {
		if errors.Is(err, services.ErrRoomNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
			return
		}
		h.logger.Error("failed to get room history",
			zap.Error(err),
			zap.String("conversation_id", conversationID.String()))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get conversation history"})
		return
	}

	RespondSuccess(c, http.StatusOK, gin.H{"messages": messages})
}

// DeleteRoom supprime une room (conversation)
// DELETE /api/v1/conversations/:id
// BE-API-010: Implement conversation delete endpoint
func (h *RoomHandler) DeleteRoom(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
		return
	}

	// Récupérer l'ID utilisateur du contexte
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Erreur déjà envoyée par GetUserIDUUID
	}

	// Supprimer la room
	if err := h.roomService.DeleteRoom(c.Request.Context(), roomID, userID); err != nil {
		if err.Error() == "room not found" || errors.Is(err, services.ErrRoomNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
			return
		}
		if err.Error() == "forbidden: only room creator can delete the room" {
			RespondWithAppError(c, apperrors.NewForbiddenError("only room creator can delete the room"))
			return
		}
		h.logger.Error("failed to delete room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to delete conversation", err))
		return
	}

	h.logger.Info("room deleted successfully",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	RespondSuccess(c, http.StatusOK, gin.H{"message": "Conversation deleted successfully"})
}

// AddParticipant ajoute un participant à une conversation
// POST /api/v1/conversations/:id/participants
// BE-API-011: Implement conversation participants endpoints
func (h *RoomHandler) AddParticipant(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
		return
	}

	// Parser la requête
	var req AddMemberRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Ajouter le participant (utilise la même logique que AddMember)
	if err := h.roomService.AddMember(c.Request.Context(), roomID, req.UserID); err != nil {
		if errors.Is(err, services.ErrRoomNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
			return
		}
		h.logger.Error("failed to add participant to room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", req.UserID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to add participant", err))
		return
	}

	h.logger.Info("participant added to room",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", req.UserID.String()))

	RespondSuccess(c, http.StatusOK, gin.H{"message": "Participant added successfully"})
}

// RemoveParticipant retire un participant d'une conversation
// DELETE /api/v1/conversations/:id/participants/:userId
// BE-API-011: Implement conversation participants endpoints
func (h *RoomHandler) RemoveParticipant(c *gin.Context) {
	// Récupérer l'ID de la room depuis l'URL
	roomIDStr := c.Param("id")
	roomID, err := uuid.Parse(roomIDStr)
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
		return
	}

	// Récupérer l'ID de l'utilisateur depuis l'URL
	userIDStr := c.Param("userId")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
		return
	}

	// Retirer le participant
	if err := h.roomService.RemoveMember(c.Request.Context(), roomID, userID); err != nil {
		if errors.Is(err, services.ErrRoomNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
			return
		}
		h.logger.Error("failed to remove participant from room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to remove participant", err))
		return
	}

	h.logger.Info("participant removed from room",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	RespondSuccess(c, http.StatusOK, gin.H{"message": "Participant removed successfully"})
}
