package comment

import (
	"api_yal/internal/logger"
	"api_yal/internal/util"
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
	"go.uber.org/zap"
)

type CommentHandler struct {
	service CommentService
	logger  *zap.Logger
}

func NewCommentHandler(service CommentService) *CommentHandler {
	return &CommentHandler{
		service: service,
		logger:  logger.Get(),
	}
}

// CreateComment создает новый комментарий
func (h *CommentHandler) CreateComment(w http.ResponseWriter, r *http.Request) {
	var req CreateCommentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("Failed to decode request",
			zap.Error(err),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
		return
	}

	// Получаем userID из контекста (устанавливается AuthMiddleware)
	userID, ok := r.Context().Value("user_id").(uint)
	if !ok {
		h.logger.Error("User ID not found in context")
		util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
		return
	}

	comment, err := h.service.CreateComment(userID, &req)
	if err != nil {
		h.logger.Error("Failed to create comment",
			zap.Error(err),
			zap.Uint("user_id", userID),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	util.ResponseWithJSON(w, http.StatusCreated, comment)
}

// GetCommentByID возвращает комментарий по ID
func (h *CommentHandler) GetCommentByID(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid comment ID",
			zap.Error(err),
			zap.String("id", idStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
		return
	}

	comment, err := h.service.GetCommentByID(uint(id))
	if err != nil {
		h.logger.Error("Failed to get comment",
			zap.Error(err),
			zap.Uint64("comment_id", id),
		)
		util.ResponseWithJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
		return
	}

	util.ResponseWithJSON(w, http.StatusOK, comment)
}

// UpdateComment обновляет комментарий
func (h *CommentHandler) UpdateComment(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid comment ID",
			zap.Error(err),
			zap.String("id", idStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
		return
	}

	var req UpdateCommentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("Failed to decode request",
			zap.Error(err),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
		return
	}

	userID, ok := r.Context().Value("user_id").(uint)
	if !ok {
		h.logger.Error("User ID not found in context")
		util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
		return
	}

	comment, err := h.service.UpdateComment(uint(id), userID, &req)
	if err != nil {
		h.logger.Error("Failed to update comment",
			zap.Error(err),
			zap.Uint64("comment_id", id),
			zap.Uint("user_id", userID),
		)
		status := http.StatusInternalServerError
		if err.Error() == "comment not found" {
			status = http.StatusNotFound
		} else if err.Error() == "you can only edit your own comments" {
			status = http.StatusForbidden
		}
		util.ResponseWithJSON(w, status, map[string]string{"error": err.Error()})
		return
	}

	util.ResponseWithJSON(w, http.StatusOK, comment)
}

// DeleteComment удаляет комментарий
func (h *CommentHandler) DeleteComment(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid comment ID",
			zap.Error(err),
			zap.String("id", idStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
		return
	}

	userID, ok := r.Context().Value("user_id").(uint)
	if !ok {
		h.logger.Error("User ID not found in context")
		util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
		return
	}

	// Проверяем, является ли пользователь администратором
	isAdmin, _ := r.Context().Value("is_admin").(bool)

	err = h.service.DeleteComment(uint(id), userID, isAdmin)
	if err != nil {
		h.logger.Error("Failed to delete comment",
			zap.Error(err),
			zap.Uint64("comment_id", id),
			zap.Uint("user_id", userID),
			zap.Bool("is_admin", isAdmin),
		)
		status := http.StatusInternalServerError
		if err.Error() == "comment not found" {
			status = http.StatusNotFound
		} else if err.Error() == "you can only delete your own comments" {
			status = http.StatusForbidden
		}
		util.ResponseWithJSON(w, status, map[string]string{"error": err.Error()})
		return
	}

	util.ResponseWithJSON(w, http.StatusOK, map[string]string{"message": "Comment deleted successfully"})
}

// ListComments возвращает список комментариев с фильтрацией
func (h *CommentHandler) ListComments(w http.ResponseWriter, r *http.Request) {
	var req ListCommentsRequest
	if err := r.ParseForm(); err != nil {
		h.logger.Error("Failed to parse form",
			zap.Error(err),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid query parameters"})
		return
	}

	// Парсим query параметры
	if pageStr := r.URL.Query().Get("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
			req.Page = page
		}
	}
	if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
		if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
			req.PageSize = size
		}
	}
	if feedbackIDStr := r.URL.Query().Get("feedback_id"); feedbackIDStr != "" {
		if id, err := strconv.ParseUint(feedbackIDStr, 10, 32); err == nil {
			idUint := uint(id)
			req.FeedbackID = &idUint
		}
	}
	if authorIDStr := r.URL.Query().Get("author_id"); authorIDStr != "" {
		if id, err := strconv.ParseUint(authorIDStr, 10, 32); err == nil {
			idUint := uint(id)
			req.AuthorID = &idUint
		}
	}
	if parentIDStr := r.URL.Query().Get("parent_id"); parentIDStr != "" {
		if id, err := strconv.ParseUint(parentIDStr, 10, 32); err == nil {
			idUint := uint(id)
			req.ParentID = &idUint
		}
	}
	if verifiedStr := r.URL.Query().Get("verified"); verifiedStr != "" {
		if verified, err := strconv.ParseBool(verifiedStr); err == nil {
			req.Verified = &verified
		}
	}
	if sortBy := r.URL.Query().Get("sort_by"); sortBy != "" {
		req.SortBy = sortBy
	}
	if sortOrder := r.URL.Query().Get("sort_order"); sortOrder != "" {
		req.SortOrder = sortOrder
	}

	comments, total, err := h.service.ListComments(&req)
	if err != nil {
		h.logger.Error("Failed to list comments",
			zap.Error(err),
			zap.Any("request", req),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	response := map[string]interface{}{
		"data":        comments,
		"total":       total,
		"page":        req.Page,
		"page_size":   req.PageSize,
		"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
	}

	util.ResponseWithJSON(w, http.StatusOK, response)
}

// GetCommentsByFeedback возвращает комментарии по отзыву
func (h *CommentHandler) GetCommentsByFeedback(w http.ResponseWriter, r *http.Request) {
	feedbackIDStr := chi.URLParam(r, "feedbackID")
	feedbackID, err := strconv.ParseUint(feedbackIDStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid feedback ID",
			zap.Error(err),
			zap.String("feedback_id", feedbackIDStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid feedback ID"})
		return
	}

	var req ListCommentsRequest
	if pageStr := r.URL.Query().Get("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
			req.Page = page
		}
	}
	if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
		if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
			req.PageSize = size
		}
	}

	comments, total, err := h.service.GetCommentsByFeedback(uint(feedbackID), &req)
	if err != nil {
		h.logger.Error("Failed to get comments by feedback",
			zap.Error(err),
			zap.Uint64("feedback_id", feedbackID),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	response := map[string]interface{}{
		"data":        comments,
		"total":       total,
		"page":        req.Page,
		"page_size":   req.PageSize,
		"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
	}

	util.ResponseWithJSON(w, http.StatusOK, response)
}

// GetMyComments возвращает комментарии текущего пользователя
func (h *CommentHandler) GetMyComments(w http.ResponseWriter, r *http.Request) {
	userID, ok := r.Context().Value("user_id").(uint)
	if !ok {
		h.logger.Error("User ID not found in context")
		util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
		return
	}

	var req ListCommentsRequest
	if pageStr := r.URL.Query().Get("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
			req.Page = page
		}
	}
	if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
		if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
			req.PageSize = size
		}
	}

	comments, total, err := h.service.GetCommentsByAuthor(userID, &req)
	if err != nil {
		h.logger.Error("Failed to get my comments",
			zap.Error(err),
			zap.Uint("user_id", userID),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	response := map[string]interface{}{
		"data":        comments,
		"total":       total,
		"page":        req.Page,
		"page_size":   req.PageSize,
		"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
	}

	util.ResponseWithJSON(w, http.StatusOK, response)
}

// GetReplies возвращает ответы на комментарий
func (h *CommentHandler) GetReplies(w http.ResponseWriter, r *http.Request) {
	parentIDStr := chi.URLParam(r, "parentID")
	parentID, err := strconv.ParseUint(parentIDStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid parent comment ID",
			zap.Error(err),
			zap.String("parent_id", parentIDStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid parent comment ID"})
		return
	}

	var req ListCommentsRequest
	if pageStr := r.URL.Query().Get("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
			req.Page = page
		}
	}
	if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
		if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
			req.PageSize = size
		}
	}

	replies, total, err := h.service.GetReplies(uint(parentID), &req)
	if err != nil {
		h.logger.Error("Failed to get replies",
			zap.Error(err),
			zap.Uint64("parent_id", parentID),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	response := map[string]interface{}{
		"data":        replies,
		"total":       total,
		"page":        req.Page,
		"page_size":   req.PageSize,
		"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
	}

	util.ResponseWithJSON(w, http.StatusOK, response)
}

// VerifyComment верифицирует комментарий (только для админов)
func (h *CommentHandler) VerifyComment(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Error("Invalid comment ID",
			zap.Error(err),
			zap.String("id", idStr),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
		return
	}

	var req struct {
		Verified bool `json:"verified"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Error("Failed to decode request",
			zap.Error(err),
		)
		util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
		return
	}

	err = h.service.VerifyComment(uint(id), req.Verified)
	if err != nil {
		h.logger.Error("Failed to verify comment",
			zap.Error(err),
			zap.Uint64("comment_id", id),
			zap.Bool("verified", req.Verified),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	status := "unverified"
	if req.Verified {
		status = "verified"
	}
	util.ResponseWithJSON(w, http.StatusOK, map[string]string{"message": "Comment " + status + " successfully"})
}

// GetCommentStats возвращает статистику по комментариям
func (h *CommentHandler) GetCommentStats(w http.ResponseWriter, r *http.Request) {
	stats, err := h.service.GetCommentStats()
	if err != nil {
		h.logger.Error("Failed to get comment stats",
			zap.Error(err),
		)
		util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	util.ResponseWithJSON(w, http.StatusOK, stats)
}