package handlers

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

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

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

// CommentServiceInterface defines the interface for comment operations
// This allows for easier testing with mocks
type CommentServiceInterface interface {
	CreateComment(ctx context.Context, trackID uuid.UUID, userID uuid.UUID, content string, timestamp float64, parentID *uuid.UUID) (*models.TrackComment, error)
	GetComments(ctx context.Context, trackID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error)
	UpdateComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, content string) (*models.TrackComment, error)
	DeleteComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, isAdmin bool) error
	GetReplies(ctx context.Context, parentID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error)
}

// CommentHandler gère les opérations sur les commentaires de tracks
type CommentHandler struct {
	commentService CommentServiceInterface
	commonHandler  *CommonHandler
}

// NewCommentHandler crée un nouveau handler de commentaires
func NewCommentHandler(commentService *services.CommentService, logger *zap.Logger) *CommentHandler {
	return &CommentHandler{
		commentService: commentService,
		commonHandler:  NewCommonHandler(logger),
	}
}

// NewCommentHandlerWithInterface crée un nouveau handler avec une interface (pour les tests)
func NewCommentHandlerWithInterface(commentService CommentServiceInterface, logger *zap.Logger) *CommentHandler {
	return &CommentHandler{
		commentService: commentService,
		commonHandler:  NewCommonHandler(logger),
	}
}

// CreateCommentRequest représente la requête pour créer un commentaire
// MOD-P1-001: Ajout tags validate pour validation systématique
type CreateCommentRequest struct {
	Content  string     `json:"content" binding:"required,min=1,max=5000" validate:"required,min=1,max=5000"`
	ParentID *uuid.UUID `json:"parent_id,omitempty" validate:"omitempty"` // Changed to *uuid.UUID
}

// UpdateCommentRequest représente la requête pour mettre à jour un commentaire
// MOD-P1-001: Ajout tags validate pour validation systématique
type UpdateCommentRequest struct {
	Content string `json:"content" binding:"required,min=1,max=5000" validate:"required,min=1,max=5000"`
}

// CreateComment gère la création d'un commentaire sur un track
// @Summary      Create comment
// @Description  Create a new comment on a track. Can be a top-level comment or a reply to another comment (using parent_id).
// @Tags         Comment
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id       path      string  true  "Track ID (UUID)"
// @Param        comment  body      handlers.CreateCommentRequest  true  "Comment data"
// @Success      201  {object}  handlers.APIResponse{data=object{comment=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      404  {object}  handlers.APIResponse "Track not found"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /tracks/{id}/comments [post]
func (h *CommentHandler) CreateComment(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Erreur déjà envoyée par GetUserIDUUID
	}
	if userID == uuid.Nil {
		// Action 1.3.2.1: Use wrapped format helper for errors
		RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "unauthorized")
		return
	}

	trackIDStr := c.Param("id")
	if trackIDStr == "" {
		// Action 1.3.2.1: Use wrapped format helper for errors
		RespondWithError(c, int(apperrors.ErrCodeValidation), "track id is required")
		return
	}

	trackID, err := uuid.Parse(trackIDStr) // Changed to uuid.Parse
	if err != nil {
		// Action 1.3.2.1: Use wrapped format helper for errors
		RespondWithError(c, int(apperrors.ErrCodeValidation), "invalid track id")
		return
	}

	var req CreateCommentRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// BE-SEC-009: Sanitize user inputs to prevent XSS and injection attacks
	req.Content = utils.SanitizeText(req.Content, 5000)

	comment, err := h.commentService.CreateComment(c.Request.Context(), trackID, userID, req.Content, 0.0, req.ParentID) // req.ParentID is already *uuid.UUID
	if err != nil {
		if errors.Is(err, services.ErrTrackNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("track"))
			return
		}
		if errors.Is(err, services.ErrParentCommentNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("parent comment"))
			return
		}
		if errors.Is(err, services.ErrParentTrackMismatch) {
			RespondWithAppError(c, apperrors.NewValidationError("parent comment does not belong to the same track"))
			return
		}
		h.commonHandler.logger.Error("failed to create comment", zap.Error(err))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to create comment", err))
		return
	}

	RespondSuccess(c, http.StatusCreated, comment)
}

// GetComments gère la récupération des commentaires d'un track
// @Summary      Get track comments
// @Description  Get paginated list of comments for a track
// @Tags         Comment
// @Accept       json
// @Produce      json
// @Param        id     path      string  true  "Track ID"
// @Param        page   query     int     false "Page number" default(1)
// @Param        limit  query     int     false "Items per page" default(20)
// @Success      200  {object}  handlers.APIResponse{data=object{comments=array,pagination=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      404  {object}  handlers.APIResponse "Track not found"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /tracks/{id}/comments [get]
func (h *CommentHandler) GetComments(c *gin.Context) {
	trackIDStr := c.Param("id")
	if trackIDStr == "" {
		RespondWithAppError(c, apperrors.NewValidationError("track id is required"))
		return
	}

	trackID, err := uuid.Parse(trackIDStr) // Changed to uuid.Parse
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid track id"))
		return
	}

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

	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 20
	}
	if limit > 100 {
		limit = 100
	}

	comments, total, err := h.commentService.GetComments(c.Request.Context(), trackID, page, limit)
	if err != nil {
		h.commonHandler.logger.Error("failed to get comments", zap.Error(err))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get comments", err))
		return
	}

	// INT-007: Standardize pagination format
	pagination := BuildPaginationData(page, limit, total)

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

// UpdateComment gère la mise à jour d'un commentaire
// @Summary      Update comment
// @Description  Update a comment (only by owner)
// @Tags         Comment
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id         path      string  true  "Comment ID (UUID)"
// @Param        comment    body      handlers.UpdateCommentRequest  true  "Updated comment content"
// @Success      200  {object}  handlers.APIResponse{data=object{comment=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      403  {object}  handlers.APIResponse "Forbidden - can only edit own comments"
// @Failure      404  {object}  handlers.APIResponse "Comment not found"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /comments/{id} [put]
func (h *CommentHandler) UpdateComment(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Erreur déjà envoyée par GetUserIDUUID
	}
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
		return
	}

	commentIDStr := c.Param("id")
	if commentIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "comment id is required"})
		return
	}

	commentID, err := uuid.Parse(commentIDStr) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment id"})
		return
	}

	var req UpdateCommentRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// BE-SEC-009: Sanitize user inputs to prevent XSS and injection attacks
	req.Content = utils.SanitizeText(req.Content, 5000)

	comment, err := h.commentService.UpdateComment(c.Request.Context(), commentID, userID, req.Content)
	if err != nil {
		if errors.Is(err, services.ErrCommentNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
			return
		}
		if errors.Is(err, services.ErrForbidden) {
			c.JSON(http.StatusForbidden, gin.H{"error": "unauthorized: you can only edit your own comments"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

// DeleteComment gère la suppression d'un commentaire
// @Summary      Delete comment
// @Description  Delete a comment (only by owner or admin)
// @Tags         Comment
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id         path      string  true  "Track ID"
// @Param        comment_id path      string  true  "Comment ID"
// @Success      200  {object}  handlers.APIResponse{data=object{message=string}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      403  {object}  handlers.APIResponse "Forbidden - not comment owner"
// @Failure      404  {object}  handlers.APIResponse "Comment not found"
// @Router       /tracks/{id}/comments/{comment_id} [delete]
func (h *CommentHandler) DeleteComment(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Erreur déjà envoyée par GetUserIDUUID
	}
	if userID == uuid.Nil {
		RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
		return
	}

	commentIDStr := c.Param("id")
	if commentIDStr == "" {
		RespondWithAppError(c, apperrors.NewValidationError("comment id is required"))
		return
	}

	commentID, err := uuid.Parse(commentIDStr) // Changed to uuid.Parse
	if err != nil {
		RespondWithAppError(c, apperrors.NewValidationError("invalid comment id"))
		return
	}

	err = h.commentService.DeleteComment(c.Request.Context(), commentID, userID, false) // Added false for isAdmin
	if err != nil {
		if errors.Is(err, services.ErrCommentNotFound) {
			RespondWithAppError(c, apperrors.NewNotFoundError("comment"))
			return
		}
		if errors.Is(err, services.ErrForbidden) {
			RespondWithAppError(c, apperrors.NewForbiddenError("you can only delete your own comments"))
			return
		}
		h.commonHandler.logger.Error("failed to delete comment", zap.Error(err))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to delete comment", err))
		return
	}

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

// GetReplies gère la récupération des réponses d'un commentaire
// @Summary      Get comment replies
// @Description  Get paginated list of replies to a comment
// @Tags         Comment
// @Accept       json
// @Produce      json
// @Param        id       path      string  true  "Parent Comment ID (UUID)"
// @Param        page     query     int     false "Page number" default(1) minimum(1)
// @Param        limit    query     int     false "Items per page" default(20) minimum(1) maximum(100)
// @Success      200  {object}  handlers.APIResponse{data=object{replies=array,pagination=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      404  {object}  handlers.APIResponse "Parent comment not found"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /comments/{id}/replies [get]
func (h *CommentHandler) GetReplies(c *gin.Context) {
	parentIDStr := c.Param("id")
	if parentIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "parent comment id is required"})
		return
	}

	parentID, err := uuid.Parse(parentIDStr) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent comment id"})
		return
	}

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

	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 20
	}
	if limit > 100 {
		limit = 100
	}

	replies, total, err := h.commentService.GetReplies(c.Request.Context(), parentID, page, limit)
	if err != nil {
		if errors.Is(err, services.ErrParentCommentNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "parent comment not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"replies": replies,
		"total":   total,
		"page":    page,
		"limit":   limit,
	})
}
