package handlers

import (
	"net/http"

	"goyco/internal/database"
	"goyco/internal/dto"
	"goyco/internal/services"

	"github.com/go-chi/chi/v5"
)

// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.

// @tag.name votes
// @tag.description Voting system endpoints. All votes are handled through the same API with identical behavior.

// @tag.name posts
// @tag.description Post management endpoints with integrated vote statistics.

// @tag.name auth
// @tag.description Authentication and user management endpoints.

// @tag.name users
// @tag.description User management endpoints.

// @tag.name api
// @tag.description API information and system metrics.

type VoteHandler struct {
	voteService *services.VoteService
}

func NewVoteHandler(voteService *services.VoteService) *VoteHandler {
	return &VoteHandler{
		voteService: voteService,
	}
}

type VoteResponse = CommonResponse

// @Summary Cast a vote on a post
// @Description Vote on a post (upvote, downvote, or remove vote). Authentication is required; the vote is performed on behalf of the current user.
// @Description
// @Description **Vote Types:**
// @Description - `up`: Upvote the post
// @Description - `down`: Downvote the post
// @Description - `none`: Remove existing vote
// @Description
// @Description **Response includes:**
// @Description - Updated post vote counts (up_votes, down_votes, score)
// @Description - Success message
// @Tags votes
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path int true "Post ID"
// @Param request body dto.CastVoteRequest true "Vote data (type: 'up', 'down', or 'none' to remove)"
// @Success 200 {object} VoteResponse "Vote cast successfully with updated post statistics"
// @Failure 401 {object} VoteResponse "Authentication required"
// @Failure 400 {object} VoteResponse "Invalid request data or vote type"
// @Failure 404 {object} VoteResponse "Post not found"
// @Failure 500 {object} VoteResponse "Internal server error"
// @Example 200 {"success": true, "message": "Vote cast successfully", "data": {"post_id": 1, "type": "up", "up_votes": 5, "down_votes": 2, "score": 3, "is_anonymous": false}}
// @Example 400 {"success": false, "error": "Invalid vote type. Must be 'up', 'down', or 'none'"}
// @Router /api/posts/{id}/vote [post]
func (h *VoteHandler) CastVote(w http.ResponseWriter, r *http.Request) {
	userID, ok := RequireAuth(w, r)
	if !ok {
		return
	}

	postID, ok := ParseUintParam(w, r, "id", "Post")
	if !ok {
		return
	}

	request, ok := GetValidatedDTO[dto.CastVoteRequest](w, r)
	if !ok {
		return
	}

	var voteType database.VoteType
	switch request.Type {
	case "up":
		voteType = database.VoteUp
	case "down":
		voteType = database.VoteDown
	case "none":
		voteType = database.VoteNone
	default:
		SendErrorResponse(w, "Invalid vote type. Must be 'up', 'down', or 'none'", http.StatusBadRequest)
		return
	}

	ipAddress := GetClientIP(r)
	userAgent := r.UserAgent()

	serviceReq := services.VoteRequest{
		UserID:    userID,
		PostID:    postID,
		Type:      voteType,
		IPAddress: ipAddress,
		UserAgent: userAgent,
	}

	response, err := h.voteService.CastVote(serviceReq)
	if err != nil {
		if err.Error() == "post not found" {
			SendErrorResponse(w, err.Error(), http.StatusNotFound)
			return
		}
		if err.Error() == "post ID is required" || err.Error() == "invalid vote type" {
			SendErrorResponse(w, err.Error(), http.StatusBadRequest)
			return
		}
		SendErrorResponse(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	SendSuccessResponse(w, "Vote cast successfully", response)
}

// @Summary Remove a vote
// @Description Remove a vote from a post for the authenticated user. This is equivalent to casting a vote with type 'none'.
// @Tags votes
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path int true "Post ID"
// @Success 200 {object} VoteResponse "Vote removed successfully with updated post statistics"
// @Failure 401 {object} VoteResponse "Authentication required"
// @Failure 400 {object} VoteResponse "Invalid post ID"
// @Failure 404 {object} VoteResponse "Post not found"
// @Failure 500 {object} VoteResponse "Internal server error"
// @Router /api/posts/{id}/vote [delete]
func (h *VoteHandler) RemoveVote(w http.ResponseWriter, r *http.Request) {
	userID, ok := RequireAuth(w, r)
	if !ok {
		return
	}

	postID, ok := ParseUintParam(w, r, "id", "Post")
	if !ok {
		return
	}

	ipAddress := GetClientIP(r)
	userAgent := r.UserAgent()

	serviceReq := services.VoteRequest{
		UserID:    userID,
		PostID:    postID,
		Type:      database.VoteNone,
		IPAddress: ipAddress,
		UserAgent: userAgent,
	}

	response, err := h.voteService.CastVote(serviceReq)
	if err != nil {
		if err.Error() == "post not found" {
			SendErrorResponse(w, err.Error(), http.StatusNotFound)
			return
		}
		if err.Error() == "post ID is required" {
			SendErrorResponse(w, err.Error(), http.StatusBadRequest)
			return
		}
		SendErrorResponse(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	SendSuccessResponse(w, "Vote removed successfully", response)
}

// @Summary Get current user's vote
// @Description Retrieve the current user's vote for a specific post. Requires authentication and returns the vote type if it exists.
// @Description
// @Description **Response:**
// @Description - If vote exists: Returns vote details with contextual metadata (including `is_anonymous`)
// @Description - If no vote: Returns success with null vote data and metadata
// @Tags votes
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path int true "Post ID"
// @Success 200 {object} VoteResponse "Vote retrieved successfully"
// @Success 200 {object} VoteResponse "No vote found for this user/post combination"
// @Failure 401 {object} VoteResponse "Authentication required"
// @Failure 400 {object} VoteResponse "Invalid post ID"
// @Failure 500 {object} VoteResponse "Internal server error"
// @Example 200 {"success": true, "message": "Vote retrieved successfully", "data": {"has_vote": true, "vote": {"type": "up", "user_id": 123}, "is_anonymous": false}}
// @Example 200 {"success": true, "message": "No vote found", "data": {"has_vote": false, "vote": null, "is_anonymous": false}}
// @Router /api/posts/{id}/vote [get]
func (h *VoteHandler) GetUserVote(w http.ResponseWriter, r *http.Request) {
	userID, ok := RequireAuth(w, r)
	if !ok {
		return
	}

	postID, ok := ParseUintParam(w, r, "id", "Post")
	if !ok {
		return
	}

	ipAddress := GetClientIP(r)
	userAgent := r.UserAgent()

	vote, err := h.voteService.GetUserVote(userID, postID, ipAddress, userAgent)
	if err != nil {
		if err.Error() == "record not found" {
			responseDTO := dto.VoteResponseDTO{
				HasVote:     false,
				Vote:        nil,
				IsAnonymous: false,
			}
			SendSuccessResponse(w, "No vote found", responseDTO)
			return
		}
		SendErrorResponse(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	voteDTO := dto.ToVoteDTO(vote)
	isAnonymous := vote.UserID == nil
	responseDTO := dto.VoteResponseDTO{
		HasVote:     true,
		Vote:        &voteDTO,
		IsAnonymous: isAnonymous,
	}
	SendSuccessResponse(w, "Vote retrieved successfully", responseDTO)
}

// @Summary Get post votes
// @Description Retrieve all votes for a specific post. Returns all votes in a single format.
// @Description
// @Description **Authentication Required:** Yes (Bearer token)
// @Description
// @Description **Response includes:**
// @Description - Array of all votes
// @Description - Total vote count
// @Description - Each vote includes type and unauthenticated status
// @Tags votes
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path int true "Post ID"
// @Success 200 {object} VoteResponse "Votes retrieved successfully with count"
// @Failure 400 {object} VoteResponse "Invalid post ID"
// @Failure 401 {object} VoteResponse "Authentication required"
// @Failure 500 {object} VoteResponse "Internal server error"
// @Example 200 {"success": true, "message": "Votes retrieved successfully", "data": {"votes": [{"type": "up", "user_id": 123}, {"type": "down", "vote_hash": "abc123"}], "count": 2}}
// @Router /api/posts/{id}/votes [get]
func (h *VoteHandler) GetPostVotes(w http.ResponseWriter, r *http.Request) {
	postID, ok := ParseUintParam(w, r, "id", "Post")
	if !ok {
		return
	}

	votes, err := h.voteService.GetPostVotes(postID)
	if err != nil {
		SendErrorResponse(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	voteDTOs := dto.ToVoteDTOs(votes)
	responseDTO := dto.VoteListDTO{
		Votes: voteDTOs,
		Count: len(voteDTOs),
	}
	SendSuccessResponse(w, "Votes retrieved successfully", responseDTO)
}

func (h *VoteHandler) MountRoutes(r chi.Router, config RouteModuleConfig) {
	protected := r
	if config.AuthMiddleware != nil {
		protected = r.With(config.AuthMiddleware)
	}
	if config.GeneralRateLimit != nil {
		protected = config.GeneralRateLimit(protected)
	}

	protected.Post("/posts/{id}/vote", WithValidation[dto.CastVoteRequest](config.ValidationMiddleware, h.CastVote))
	protected.Delete("/posts/{id}/vote", h.RemoveVote)
	protected.Get("/posts/{id}/vote", h.GetUserVote)
	protected.Get("/posts/{id}/votes", h.GetPostVotes)
}
