package feetback

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

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

// FeedbackHandler обработчик для отзывов
type FeedbackHandler struct {
	service FeedbackService
	log     *zap.Logger
}

// NewFeedbackHandler создает новый обработчик
func NewFeedbackHandler(service FeedbackService) *FeedbackHandler {
	return &FeedbackHandler{
		service: service,
		log:     logger.Get(),
	}
}

// getUserIDFromContext получает ID пользователя из контекста
func (h *FeedbackHandler) getUserIDFromContext(r *http.Request) (uint, bool) {
	userID, ok := r.Context().Value("user_id").(uint)
	return userID, ok
}

// isAdminFromContext проверяет, является ли пользователь админом
func (h *FeedbackHandler) isAdminFromContext(r *http.Request) bool {
	isAdmin, ok := r.Context().Value("is_admin").(bool)
	return ok && isAdmin
}

// parsePagination парсит параметры пагинации
func (h *FeedbackHandler) parsePagination(r *http.Request) (offset, limit int) {
	offset, _ = strconv.Atoi(r.URL.Query().Get("offset"))
	limit, _ = strconv.Atoi(r.URL.Query().Get("limit"))

	if limit <= 0 || limit > 100 {
		limit = 20
	}
	if offset < 0 {
		offset = 0
	}
	return
}

// sendJSON отправляет JSON ответ
func (h *FeedbackHandler) sendJSON(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	if err := json.NewEncoder(w).Encode(data); err != nil {
		h.log.Error("Failed to encode JSON response", zap.Error(err))
	}
}

// sendError отправляет ошибку
func (h *FeedbackHandler) sendError(w http.ResponseWriter, status int, message string) {
	h.sendJSON(w, status, ErrorResponse{
		Error:   http.StatusText(status),
		Message: message,
		Code:    status,
	})
}

// CreateFeedback создает новый отзыв
// POST /feedbacks
func (h *FeedbackHandler) CreateFeedback(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	var req CreateFeedbackRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	feedback := &models.Feedback{
		ObjectID: req.ObjectID,
		Score:    req.Rating,
		Text:     req.Text,
		Platform: req.Platform,
	}

	if err := h.service.Create(feedback, userID); err != nil {
		h.log.Error("Failed to create feedback", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to create feedback")
		return
	}

	// Загружаем созданный отзыв с данными владельца и объекта
	created, _ := h.service.GetByID(feedback.ID)
	response := h.mapFeedbackToResponse(created)

	h.sendJSON(w, http.StatusCreated, response)
}

// GetFeedbackByID получает отзыв по ID
// GET /feedbacks/{id}
func (h *FeedbackHandler) GetFeedbackByID(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid feedback id")
		return
	}

	feedback, err := h.service.GetByID(uint(id))
	if err != nil {
		h.sendError(w, http.StatusNotFound, "feedback not found")
		return
	}

	response := h.mapFeedbackToResponse(feedback)
	h.sendJSON(w, http.StatusOK, response)
}

// UpdateFeedback обновляет отзыв
// PUT /feedbacks/{id}
func (h *FeedbackHandler) UpdateFeedback(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid feedback id")
		return
	}

	var req UpdateFeedbackRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	updates := make(map[string]interface{})
	if req.Rating != nil {
		updates["rating"] = *req.Rating
	}
	if req.Text != nil {
		updates["text"] = *req.Text
	}
	if req.Platform != nil {
		updates["platform"] = *req.Platform
	}
	if req.MediaURLs != nil {
		updates["media_urls"] = req.MediaURLs
	}

	if err := h.service.Update(uint(id), userID, updates); err != nil {
		if err.Error() == "you can only update your own feedback" {
			h.sendError(w, http.StatusForbidden, err.Error())
			return
		}
		h.log.Error("Failed to update feedback", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to update feedback")
		return
	}

	feedback, _ := h.service.GetByID(uint(id))
	response := h.mapFeedbackToResponse(feedback)
	h.sendJSON(w, http.StatusOK, response)
}

// DeleteFeedback удаляет отзыв
// DELETE /feedbacks/{id}
func (h *FeedbackHandler) DeleteFeedback(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid feedback id")
		return
	}

	isAdmin := h.isAdminFromContext(r)

	if err := h.service.Delete(uint(id), userID, isAdmin); err != nil {
		if err.Error() == "you can only delete your own feedback" {
			h.sendError(w, http.StatusForbidden, err.Error())
			return
		}
		h.log.Error("Failed to delete feedback", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to delete feedback")
		return
	}

	h.sendJSON(w, http.StatusNoContent, nil)
}

// ListFeedbacks возвращает список отзывов с пагинацией
// GET /feedbacks
func (h *FeedbackHandler) ListFeedbacks(w http.ResponseWriter, r *http.Request) {
	offset, limit := h.parsePagination(r)

	feedbacks, total, err := h.service.List(offset, limit)
	if err != nil {
		h.log.Error("Failed to list feedbacks", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to list feedbacks")
		return
	}

	response := h.mapFeedbackListToResponse(feedbacks, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// GetMyFeedbacks возвращает отзывы текущего пользователя
// GET /feedbacks/my
func (h *FeedbackHandler) GetMyFeedbacks(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	offset, limit := h.parsePagination(r)

	feedbacks, total, err := h.service.ListByOwner(userID, offset, limit)
	if err != nil {
		h.log.Error("Failed to list user feedbacks", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to list feedbacks")
		return
	}

	response := h.mapFeedbackListToResponse(feedbacks, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// GetFeedbacksByObject возвращает отзывы по объекту
// GET /feedbacks/object/{objectID}
func (h *FeedbackHandler) GetFeedbacksByObject(w http.ResponseWriter, r *http.Request) {
	objectIDStr := chi.URLParam(r, "objectID")
	objectID, err := strconv.ParseUint(objectIDStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid object id")
		return
	}

	offset, limit := h.parsePagination(r)

	feedbacks, total, err := h.service.ListByObject(uint(objectID), offset, limit)
	if err != nil {
		h.log.Error("Failed to list feedbacks by object", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to list feedbacks")
		return
	}

	response := h.mapFeedbackListToResponse(feedbacks, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// GetFeedbacksByPlatform возвращает отзывы по платформе
// GET /feedbacks/platform/{platform}
func (h *FeedbackHandler) GetFeedbacksByPlatform(w http.ResponseWriter, r *http.Request) {
	platformStr := chi.URLParam(r, "platform")
	platform := models.PlatformType(platformStr)

	offset, limit := h.parsePagination(r)

	feedbacks, total, err := h.service.ListByPlatform(platform, offset, limit)
	if err != nil {
		h.log.Error("Failed to list feedbacks by platform", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to list feedbacks")
		return
	}

	response := h.mapFeedbackListToResponse(feedbacks, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// SearchFeedbacks ищет отзывы по тексту
// GET /feedbacks/search
func (h *FeedbackHandler) SearchFeedbacks(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("q")
	if query == "" {
		h.sendError(w, http.StatusBadRequest, "search query is required")
		return
	}

	offset, limit := h.parsePagination(r)

	feedbacks, total, err := h.service.Search(query, offset, limit)
	if err != nil {
		h.log.Error("Failed to search feedbacks", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to search feedbacks")
		return
	}

	response := h.mapFeedbackListToResponse(feedbacks, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// GetFeedbackComments возвращает комментарии к отзыву
// GET /feedbacks/{id}/comments
func (h *FeedbackHandler) GetFeedbackComments(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid feedback id")
		return
	}

	offset, limit := h.parsePagination(r)

	comments, total, err := h.service.GetComments(uint(id), offset, limit)
	if err != nil {
		h.log.Error("Failed to get comments", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to get comments")
		return
	}

	response := h.mapCommentListToResponse(comments, total, offset, limit)
	h.sendJSON(w, http.StatusOK, response)
}

// AddComment добавляет комментарий к отзыву
// POST /feedbacks/{id}/comments
func (h *FeedbackHandler) AddComment(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid feedback id")
		return
	}

	var req CreateCommentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	comment, err := h.service.AddComment(uint(id), userID, req.Text)
	if err != nil {
		if err.Error() == "feedback not found" {
			h.sendError(w, http.StatusNotFound, err.Error())
			return
		}
		h.log.Error("Failed to add comment", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to add comment")
		return
	}

	response := h.mapCommentToResponse(comment)
	h.sendJSON(w, http.StatusCreated, response)
}

// UpdateComment обновляет комментарий
// PUT /feedbacks/{id}/comments/{commentID}
func (h *FeedbackHandler) UpdateComment(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	commentIDStr := chi.URLParam(r, "commentID")
	commentID, err := strconv.ParseUint(commentIDStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid comment id")
		return
	}

	var req UpdateCommentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	isAdmin := h.isAdminFromContext(r)

	if err := h.service.UpdateComment(uint(commentID), userID, req.Text, isAdmin); err != nil {
		if err.Error() == "you can only update your own comments" {
			h.sendError(w, http.StatusForbidden, err.Error())
			return
		}
		h.log.Error("Failed to update comment", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to update comment")
		return
	}

	h.sendJSON(w, http.StatusOK, map[string]string{"message": "comment updated successfully"})
}

// DeleteComment удаляет комментарий
// DELETE /feedbacks/{id}/comments/{commentID}
func (h *FeedbackHandler) DeleteComment(w http.ResponseWriter, r *http.Request) {
	userID, ok := h.getUserIDFromContext(r)
	if !ok {
		h.sendError(w, http.StatusUnauthorized, "unauthorized")
		return
	}

	commentIDStr := chi.URLParam(r, "commentID")
	commentID, err := strconv.ParseUint(commentIDStr, 10, 32)
	if err != nil {
		h.sendError(w, http.StatusBadRequest, "invalid comment id")
		return
	}

	isAdmin := h.isAdminFromContext(r)

	if err := h.service.DeleteComment(uint(commentID), userID, isAdmin); err != nil {
		if err.Error() == "you can only delete your own comments" {
			h.sendError(w, http.StatusForbidden, err.Error())
			return
		}
		h.log.Error("Failed to delete comment", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to delete comment")
		return
	}

	h.sendJSON(w, http.StatusNoContent, nil)
}

// GetFeedbackStats возвращает статистику по отзывам
// GET /feedbacks/stats
func (h *FeedbackHandler) GetFeedbackStats(w http.ResponseWriter, r *http.Request) {
	stats, err := h.service.GetStats()
	if err != nil {
		h.log.Error("Failed to get stats", zap.Error(err))
		h.sendError(w, http.StatusInternalServerError, "failed to get stats")
		return
	}

	h.sendJSON(w, http.StatusOK, stats)
}

// mapFeedbackToResponse маппинг модели в DTO ответа
func (h *FeedbackHandler) mapFeedbackToResponse(f *models.Feedback) FeedbackResponse {
	resp := FeedbackResponse{
		ID:           f.ID,
		OwnerID:      f.OwnerID,
		ObjectID:     f.ObjectID,
		Rating:       f.Score,
		Text:         f.Text,
		Platform:     f.Platform,
		CommentCount: f.CommentCount,
		CreatedAt:    f.CreatedAt,
		UpdatedAt:    f.UpdatedAt,
	}

	if f.OwnerID != 0 {
		resp.Owner = &AccountBriefResponse{
			ID:        f.Owner.ID,
			Username:  f.Owner.FullName,
		}
	}

	if f.ObjectID != 0 {
		resp.Object = &ObjectBriefResponse{
			ID:          f.Object.ID,
			Name:        f.Object.Owner.FullName,
			Description: f.Object.ShortDescription,
		}
	}

	return resp
}

// mapFeedbackListToResponse маппинг списка отзывов в DTO ответа
func (h *FeedbackHandler) mapFeedbackListToResponse(feedbacks []models.Feedback, total int64, offset, limit int) FeedbackListResponse {
	data := make([]FeedbackResponse, len(feedbacks))
	for i, f := range feedbacks {
		data[i] = h.mapFeedbackToResponse(&f)
	}

	resp := FeedbackListResponse{
		Data:   data,
		Total:  total,
		Offset: offset,
		Limit:  limit,
	}

	nextOffset := offset + limit
	if int64(nextOffset) < total {
		resp.NextOffset = &nextOffset
	}

	return resp
}

// mapCommentToResponse маппинг комментария в DTO ответа
func (h *FeedbackHandler) mapCommentToResponse(c *models.Comment) CommentResponse {
	resp := CommentResponse{
		ID:         c.ID,
		FeedbackID: c.FeedbackID,
		AccountID:  c.AuthorID,
		Text:       c.Text,
		CreatedAt:  c.CreatedAt,
		UpdatedAt:  c.UpdatedAt,
	}

	if c.AuthorID != 0 {
		resp.Account = &AccountBriefResponse{
			ID:        c.Author.ID,
			Username:  c.Author.FullName,
		}
	}

	return resp
}

// mapCommentListToResponse маппинг списка комментариев в DTO ответа
func (h *FeedbackHandler) mapCommentListToResponse(comments []models.Comment, total, offset, limit int) CommentListResponse {
	data := make([]CommentResponse, len(comments))
	for i, c := range comments {
		data[i] = h.mapCommentToResponse(&c)
	}

	resp := CommentListResponse{
		Data:   data,
		Total:  total,
		Offset: offset,
		Limit:  limit,
	}

	nextOffset := offset + limit
	if nextOffset < total {
		resp.NextOffset = &nextOffset
	}

	return resp
}