package handler

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

	"eslogad-be/internal/appcontext"
	"eslogad-be/internal/constants"
	"eslogad-be/internal/contract"
	"eslogad-be/internal/logger"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type VoteEventService interface {
	CreateVoteEvent(ctx context.Context, req *contract.CreateVoteEventRequest) (*contract.VoteEventResponse, error)
	GetVoteEventByID(ctx context.Context, id uuid.UUID) (*contract.VoteEventResponse, error)
	GetActiveEvents(ctx context.Context) ([]contract.VoteEventResponse, error)
	ListVoteEvents(ctx context.Context, req *contract.ListVoteEventsRequest) (*contract.ListVoteEventsResponse, error)
	UpdateVoteEvent(ctx context.Context, id uuid.UUID, req *contract.UpdateVoteEventRequest) (*contract.VoteEventResponse, error)
	DeleteVoteEvent(ctx context.Context, id uuid.UUID) error
	CreateCandidate(ctx context.Context, req *contract.CreateCandidateRequest) (*contract.CandidateResponse, error)
	GetCandidates(ctx context.Context, eventID uuid.UUID) ([]contract.CandidateResponse, error)
	SubmitVote(ctx context.Context, userID uuid.UUID, req *contract.SubmitVoteRequest) (*contract.VoteResponse, error)
	GetVoteResults(ctx context.Context, eventID uuid.UUID) (*contract.VoteResultsResponse, error)
	CheckVoteStatus(ctx context.Context, userID, eventID uuid.UUID) (*contract.CheckVoteStatusResponse, error)
}

type VoteEventHandler struct {
	voteEventService VoteEventService
}

func NewVoteEventHandler(voteEventService VoteEventService) *VoteEventHandler {
	return &VoteEventHandler{
		voteEventService: voteEventService,
	}
}

func (h *VoteEventHandler) CreateVoteEvent(c *gin.Context) {
	var req contract.CreateVoteEventRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CreateVoteEvent -> request binding failed")
		h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
		return
	}

	voteEventResponse, err := h.voteEventService.CreateVoteEvent(c.Request.Context(), &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CreateVoteEvent -> Failed to create vote event")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::CreateVoteEvent -> Successfully created vote event = %+v", voteEventResponse)
	c.JSON(http.StatusCreated, contract.BuildSuccessResponse(voteEventResponse))
}

func (h *VoteEventHandler) GetVoteEvent(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteEvent -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	voteEventResponse, err := h.voteEventService.GetVoteEventByID(c.Request.Context(), eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteEvent -> Failed to get vote event")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::GetVoteEvent -> Successfully retrieved vote event = %+v", voteEventResponse)
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(voteEventResponse))
}

func (h *VoteEventHandler) GetActiveEvents(c *gin.Context) {
	events, err := h.voteEventService.GetActiveEvents(c.Request.Context())
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetActiveEvents -> Failed to get active events")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::GetActiveEvents -> Successfully retrieved %d active events", len(events))
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
		"events": events,
		"count":  len(events),
	}))
}

func (h *VoteEventHandler) ListVoteEvents(c *gin.Context) {
	req := &contract.ListVoteEventsRequest{
		Page:  1,
		Limit: 10,
	}

	if page := c.Query("page"); page != "" {
		if p, err := strconv.Atoi(page); err == nil && p > 0 {
			req.Page = p
		}
	}

	if limit := c.Query("limit"); limit != "" {
		if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 {
			req.Limit = l
		}
	}

	voteEventsResponse, err := h.voteEventService.ListVoteEvents(c.Request.Context(), req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::ListVoteEvents -> Failed to list vote events")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::ListVoteEvents -> Successfully listed vote events")
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(voteEventsResponse))
}

func (h *VoteEventHandler) UpdateVoteEvent(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::UpdateVoteEvent -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	var req contract.UpdateVoteEventRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::UpdateVoteEvent -> request binding failed")
		h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
		return
	}

	voteEventResponse, err := h.voteEventService.UpdateVoteEvent(c.Request.Context(), eventID, &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::UpdateVoteEvent -> Failed to update vote event")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::UpdateVoteEvent -> Successfully updated vote event = %+v", voteEventResponse)
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(voteEventResponse))
}

func (h *VoteEventHandler) DeleteVoteEvent(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::DeleteVoteEvent -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	err = h.voteEventService.DeleteVoteEvent(c.Request.Context(), eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::DeleteVoteEvent -> Failed to delete vote event")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Info("VoteEventHandler::DeleteVoteEvent -> Successfully deleted vote event")
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{
		"message": "Vote event deleted successfully",
	}))
}

func (h *VoteEventHandler) CreateCandidate(c *gin.Context) {
	var req contract.CreateCandidateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CreateCandidate -> request binding failed")
		h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
		return
	}

	candidateResponse, err := h.voteEventService.CreateCandidate(c.Request.Context(), &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CreateCandidate -> Failed to create candidate")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::CreateCandidate -> Successfully created candidate = %+v", candidateResponse)
	c.JSON(http.StatusCreated, contract.BuildSuccessResponse(candidateResponse))
}

func (h *VoteEventHandler) GetCandidates(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetCandidates -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	candidates, err := h.voteEventService.GetCandidates(c.Request.Context(), eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetCandidates -> Failed to get candidates")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::GetCandidates -> Successfully retrieved %d candidates", len(candidates))
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
		"candidates": candidates,
		"count":      len(candidates),
	}))
}

func (h *VoteEventHandler) SubmitVote(c *gin.Context) {
	appCtx := appcontext.FromGinContext(c.Request.Context())
	if appCtx.UserID == uuid.Nil {
		h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
		return
	}

	var req contract.SubmitVoteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::SubmitVote -> request binding failed")
		h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
		return
	}

	voteResponse, err := h.voteEventService.SubmitVote(c.Request.Context(), appCtx.UserID, &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::SubmitVote -> Failed to submit vote")
		h.sendErrorResponse(c, err.Error(), http.StatusBadRequest)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::SubmitVote -> Successfully submitted vote = %+v", voteResponse)
	c.JSON(http.StatusCreated, contract.BuildSuccessResponse(voteResponse))
}

func (h *VoteEventHandler) GetVoteResults(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteResults -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	voteEvent, err := h.voteEventService.GetVoteEventByID(c.Request.Context(), eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteResults -> Failed to get vote event")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	if !voteEvent.ResultsOpen {
		logger.FromContext(c).Info("VoteEventHandler::GetVoteResults -> Results not open for viewing")
		h.sendErrorResponse(c, "Results are not open for viewing", http.StatusForbidden)
		return
	}

	results, err := h.voteEventService.GetVoteResults(c.Request.Context(), eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteResults -> Failed to get vote results")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::GetVoteResults -> Successfully retrieved vote results")
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(results))
}

func (h *VoteEventHandler) CheckVoteStatus(c *gin.Context) {
	appCtx := appcontext.FromGinContext(c.Request.Context())
	if appCtx.UserID == uuid.Nil {
		h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
		return
	}

	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CheckVoteStatus -> Invalid event ID")
		h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode)
		return
	}

	status, err := h.voteEventService.CheckVoteStatus(c.Request.Context(), appCtx.UserID, eventID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("VoteEventHandler::CheckVoteStatus -> Failed to check vote status")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("VoteEventHandler::CheckVoteStatus -> Successfully checked vote status")
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(status))
}

func (h *VoteEventHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
	errorResponse := &contract.ErrorResponse{
		Error:   message,
		Code:    statusCode,
		Details: map[string]interface{}{},
	}
	c.JSON(statusCode, errorResponse)
}

func (h *VoteEventHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) {
	statusCode := constants.HttpErrorMap[errorCode]
	if statusCode == 0 {
		statusCode = http.StatusBadRequest
	}

	errorResponse := &contract.ErrorResponse{
		Error: message,
		Code:  statusCode,
		Details: map[string]interface{}{
			"error_code": errorCode,
			"entity":     "vote_event",
		},
	}
	c.JSON(statusCode, errorResponse)
}