package handlers

import (
	"api_bb/internal/models"
	"api_bb/internal/service"
	"api_bb/pkg/logger"
	"api_bb/pkg/middleware"
	"api_bb/pkg/utils"
	"net/http"
	"strconv"

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

type NewsHandler struct {
	newsService service.NewsService
	logger      logger.LoggerInterface
	validator   *validator.Validate
}

func NewNewsHandler(newsService service.NewsService, log logger.LoggerInterface) *NewsHandler {
	return &NewsHandler{
		newsService: newsService,
		logger:      log,
		validator:   validator.New(),
	}
}

// GetNews возвращает список новостей с пагинацией и фильтрацией
func (h *NewsHandler) GetNews(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start GetNews Method")
	
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
	offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
	category := r.URL.Query().Get("category")

	h.logger.Debug("GetNews parameters",
		zap.Int("limit", limit),
		zap.Int("offset", offset),
		zap.String("category", category),
	)

	if limit == 0 {
		limit = 10
	}
	if limit > 50 {
		limit = 50
	}

	news, total, err := h.newsService.GetAllNews(limit, offset, category)
	if err != nil {
		h.logger.Error("Failed to get news", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get news")
		return
	}

	h.logger.Debug("Successfully retrieved news",
		zap.Int("count", len(news)),
		zap.Int("total", int(total)),
	)

	h.logger.Debug("End GetNews Method")
	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"news":   news,
		"total":  total,
		"limit":  limit,
		"offset": offset,
	})
}

// GetNewsByID возвращает конкретную новость
func (h *NewsHandler) GetNewsByID(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start GetNewsByID Method")
	
	idStr := chi.URLParam(r, "id")
	h.logger.Debug("GetNewsByID parameters", zap.String("id", idStr))
	
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid news ID", zap.String("id", idStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
		return
	}

	news, err := h.newsService.GetNewsByID(uint(id))
	if err != nil {
		h.logger.Warn("News not found", zap.Uint("id", uint(id)), zap.Error(err))
		utils.RespondWithError(w, http.StatusNotFound, "News not found")
		return
	}

	h.logger.Debug("Successfully retrieved news by ID", zap.Uint("id", uint(id)))
	h.logger.Debug("End GetNewsByID Method")
	utils.RespondWithJSON(w, http.StatusOK, news)
}

// CreateNews создает новую новость
func (h *NewsHandler) CreateNews(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start CreateNews Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("CreateNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in CreateNews",
			zap.String("path", r.URL.Path),
			zap.String("method", r.Method),
			zap.String("remote_addr", r.RemoteAddr),
		)
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	var req models.CreateNewsRequest
	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Warn("Invalid request body in CreateNews", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
		return
	}

	h.logger.Debug("CreateNews request data", 
		zap.String("title", req.Title),
		zap.String("category", string(req.Category)),
	)

	if err := h.validator.Struct(req); err != nil {
		h.logger.Warn("Validation failed in CreateNews", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
		return
	}

	news, err := h.newsService.CreateNews(req, userID)
	if err != nil {
		h.logger.Error("Failed to create news", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create news")
		return
	}

	h.logger.Info("Successfully created news", 
		zap.Uint("newsID", news.ID),
		zap.Uint("userID", userID),
	)
	h.logger.Debug("End CreateNews Method")
	utils.RespondWithJSON(w, http.StatusCreated, news)
}

// UpdateNews обновляет новость
func (h *NewsHandler) UpdateNews(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start UpdateNews Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("UpdateNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in UpdateNews")
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	idStr := chi.URLParam(r, "id")
	h.logger.Debug("UpdateNews parameters", zap.String("id", idStr))
	
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid news ID in UpdateNews", zap.String("id", idStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
		return
	}

	var req models.UpdateNewsRequest
	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Warn("Invalid request body in UpdateNews", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
		return
	}

	h.logger.Debug("UpdateNews request data", 
		zap.String("title", req.Title),
		zap.String("category", string(req.Category)),
	)

	if err := h.validator.Struct(req); err != nil {
		h.logger.Warn("Validation failed in UpdateNews", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
		return
	}

	news, err := h.newsService.UpdateNews(uint(id), req, userID)
	if err != nil {
		if err.Error() == "access denied" {
			h.logger.Warn("Access denied in UpdateNews", 
				zap.Uint("userID", userID),
				zap.Uint("newsID", uint(id)),
			)
			utils.RespondWithError(w, http.StatusForbidden, "Access denied")
			return
		}
		h.logger.Error("Failed to update news", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update news")
		return
	}

	h.logger.Info("Successfully updated news", 
		zap.Uint("newsID", uint(id)),
		zap.Uint("userID", userID),
	)
	h.logger.Debug("End UpdateNews Method")
	utils.RespondWithJSON(w, http.StatusOK, news)
}

// DeleteNews удаляет новость
func (h *NewsHandler) DeleteNews(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start DeleteNews Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("DeleteNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in DeleteNews")
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	idStr := chi.URLParam(r, "id")
	h.logger.Debug("DeleteNews parameters", zap.String("id", idStr))
	
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid news ID in DeleteNews", zap.String("id", idStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
		return
	}

	err = h.newsService.DeleteNews(uint(id), userID)
	if err != nil {
		if err.Error() == "access denied" {
			h.logger.Warn("Access denied in DeleteNews", 
				zap.Uint("userID", userID),
				zap.Uint("newsID", uint(id)),
			)
			utils.RespondWithError(w, http.StatusForbidden, "Access denied")
			return
		}
		h.logger.Error("Failed to delete news", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete news")
		return
	}

	h.logger.Info("Successfully deleted news", 
		zap.Uint("newsID", uint(id)),
		zap.Uint("userID", userID),
	)
	h.logger.Debug("End DeleteNews Method")
	utils.RespondWithJSON(w, http.StatusOK, map[string]string{"message": "News deleted successfully"})
}

// CreateComment создает комментарий к новости
func (h *NewsHandler) CreateComment(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start CreateComment Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("CreateComment user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in CreateComment")
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	newsIDStr := chi.URLParam(r, "id")
	h.logger.Debug("CreateComment parameters", zap.String("newsID", newsIDStr))
	
	newsID, err := strconv.ParseUint(newsIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid news ID in CreateComment", zap.String("newsID", newsIDStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
		return
	}

	var req models.CreateCommentRequest
	if err := utils.DecodeJSONBody(w, r, &req); err != nil {
		h.logger.Warn("Invalid request body in CreateComment", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
		return
	}

	h.logger.Debug("CreateComment request data", 
		zap.String("content", req.Content),
	)

	if err := h.validator.Struct(req); err != nil {
		h.logger.Warn("Validation failed in CreateComment", zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
		return
	}

	comment, err := h.newsService.CreateComment(uint(newsID), req, userID)
	if err != nil {
		h.logger.Error("Failed to create comment", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create comment")
		return
	}

	h.logger.Info("Successfully created comment", 
		zap.Uint("commentID", comment.ID),
		zap.Uint("newsID", uint(newsID)),
		zap.Uint("userID", userID),
	)
	h.logger.Debug("End CreateComment Method")
	utils.RespondWithJSON(w, http.StatusCreated, comment)
}

// GetComments возвращает комментарии к новости
func (h *NewsHandler) GetComments(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start GetComments Method")
	
	newsIDStr := chi.URLParam(r, "id")
	h.logger.Debug("GetComments parameters", zap.String("newsID", newsIDStr))
	
	newsID, err := strconv.ParseUint(newsIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid news ID in GetComments", zap.String("newsID", newsIDStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
		return
	}

	comments, err := h.newsService.GetCommentsByNewsID(uint(newsID))
	if err != nil {
		h.logger.Error("Failed to get comments", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get comments")
		return
	}

	h.logger.Debug("Successfully retrieved comments", 
		zap.Uint("newsID", uint(newsID)),
		zap.Int("count", len(comments)),
	)
	h.logger.Debug("End GetComments Method")
	utils.RespondWithJSON(w, http.StatusOK, comments)
}

// DeleteComment удаляет комментарий
func (h *NewsHandler) DeleteComment(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start DeleteComment Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("DeleteComment user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in DeleteComment")
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	commentIDStr := chi.URLParam(r, "commentId")
	h.logger.Debug("DeleteComment parameters", zap.String("commentID", commentIDStr))
	
	commentID, err := strconv.ParseUint(commentIDStr, 10, 32)
	if err != nil {
		h.logger.Warn("Invalid comment ID in DeleteComment", zap.String("commentID", commentIDStr), zap.Error(err))
		utils.RespondWithError(w, http.StatusBadRequest, "Invalid comment ID")
		return
	}

	err = h.newsService.DeleteComment(uint(commentID), userID)
	if err != nil {
		if err.Error() == "access denied" {
			h.logger.Warn("Access denied in DeleteComment", 
				zap.Uint("userID", userID),
				zap.Uint("commentID", uint(commentID)),
			)
			utils.RespondWithError(w, http.StatusForbidden, "Access denied")
			return
		}
		h.logger.Error("Failed to delete comment", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete comment")
		return
	}

	h.logger.Info("Successfully deleted comment", 
		zap.Uint("commentID", uint(commentID)),
		zap.Uint("userID", userID),
	)
	h.logger.Debug("End DeleteComment Method")
	utils.RespondWithJSON(w, http.StatusOK, map[string]string{"message": "Comment deleted successfully"})
}

// GetUserNews возвращает новости конкретного пользователя
func (h *NewsHandler) GetUserNews(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("Start GetUserNews Method")
	
	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	h.logger.Debug("GetUserNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
	
	if !ok {
		h.logger.Warn("Failed to get userID from context in GetUserNews")
		utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
	offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))

	h.logger.Debug("GetUserNews parameters",
		zap.Int("limit", limit),
		zap.Int("offset", offset),
	)

	if limit == 0 {
		limit = 10
	}

	news, total, err := h.newsService.GetUserNews(userID, limit, offset)
	if err != nil {
		h.logger.Error("Failed to get user news", zap.Error(err))
		utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user news")
		return
	}

	h.logger.Debug("Successfully retrieved user news",
		zap.Uint("userID", userID),
		zap.Int("count", len(news)),
		zap.Int("total", int(total)),
	)
	h.logger.Debug("End GetUserNews Method")
	utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
		"news":  news,
		"total": total,
	})
}