package handler

import (
	"apskel-pos-be/internal/appcontext"
	"net/http"
	"strconv"

	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/logger"
	"apskel-pos-be/internal/transformer"

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

type UserHandler struct {
	userService   UserService
	userValidator UserValidator
}

func NewUserHandler(userService UserService, userValidator UserValidator) *UserHandler {
	return &UserHandler{
		userService:   userService,
		userValidator: userValidator,
	}
}

func (h *UserHandler) CreateUser(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

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

	validationError, validationErrorCode := h.userValidator.ValidateCreateUserRequest(&req)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::CreateUser -> request validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	userResponse, err := h.userService.CreateUser(c.Request.Context(), &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::CreateUser -> Failed to create user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::CreateUser -> Successfully created user = %+v", userResponse)
	c.JSON(http.StatusCreated, userResponse)
}

func (h *UserHandler) UpdateUser(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUser -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::UpdateUser -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

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

	validationError, validationErrorCode = h.userValidator.ValidateUpdateUserRequest(&req)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::UpdateUser -> request validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	userResponse, err := h.userService.UpdateUser(c.Request.Context(), userID, &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUser -> Failed to update user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::UpdateUser -> Successfully updated user = %+v", userResponse)
	c.JSON(http.StatusOK, userResponse)
}

func (h *UserHandler) DeleteUser(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::DeleteUser -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::DeleteUser -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	err = h.userService.DeleteUser(c.Request.Context(), userID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::DeleteUser -> Failed to delete user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Info("UserHandler::DeleteUser -> Successfully deleted user")
	c.JSON(http.StatusOK, transformer.CreateSuccessResponse("User deleted successfully", nil))
}

func (h *UserHandler) GetUser(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::GetUser -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::GetUser -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	userResponse, err := h.userService.GetUserByID(c.Request.Context(), userID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::GetUser -> Failed to get user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::GetUser -> Successfully retrieved user = %+v", userResponse)
	c.JSON(http.StatusOK, userResponse)
}

func (h *UserHandler) ListUsers(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	req := &contract.ListUsersRequest{
		Page:  1,
		Limit: 10,
	}

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

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

	if role := c.Query("role"); role != "" {
		req.Role = &role
	}

	if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
		if outletID, err := uuid.Parse(outletIDStr); err == nil {
			req.OutletID = &outletID
		}
	}

	if isActiveStr := c.Query("is_active"); isActiveStr != "" {
		if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
			req.IsActive = &isActive
		}
	}

	validationError, validationErrorCode := h.userValidator.ValidateListUsersRequest(req)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::ListUsers -> request validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	req.OrganizationID = &contextInfo.OrganizationID
	usersResponse, err := h.userService.ListUsers(c.Request.Context(), req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::ListUsers -> Failed to list users from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users = %+v", usersResponse)
	c.JSON(http.StatusOK, contract.BuildSuccessResponse(usersResponse))
}

func (h *UserHandler) ChangePassword(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangePassword -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

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

	validationError, validationErrorCode = h.userValidator.ValidateChangePasswordRequest(&req)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangePassword -> request validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	err = h.userService.ChangePassword(c.Request.Context(), userID, &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> Failed to change password from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Info("UserHandler::ChangePassword -> Successfully changed password")
	c.JSON(http.StatusOK, transformer.CreateSuccessResponse("Password changed successfully", nil))
}

func (h *UserHandler) ActivateUser(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::ActivateUser -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::ActivateUser -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	err = h.userService.ActivateUser(c.Request.Context(), userID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::ActivateUser -> Failed to activate user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Info("UserHandler::ActivateUser -> Successfully activated user")
	c.JSON(http.StatusOK, transformer.CreateSuccessResponse("User activated successfully", nil))
}

func (h *UserHandler) DeactivateUser(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::DeactivateUser -> Invalid user ID")
		h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
		return
	}

	validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
	if validationError != nil {
		logger.FromContext(c).WithError(validationError).Error("UserHandler::DeactivateUser -> user ID validation failed")
		h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
		return
	}

	err = h.userService.DeactivateUser(c.Request.Context(), userID)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::DeactivateUser -> Failed to deactivate user from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::DeactivateUser -> Successfully deactivated user with ID: %s", userID.String())
	c.JSON(http.StatusOK, gin.H{"message": "User deactivated successfully"})
}

func (h *UserHandler) UpdateUserOutlet(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

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

	userResponse, err := h.userService.UpdateUserOutlet(c.Request.Context(), contextInfo.UserID, &req)
	if err != nil {
		logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUserOutlet -> Failed to update user outlet from service")
		h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
		return
	}

	logger.FromContext(c).Infof("UserHandler::UpdateUserOutlet -> Successfully updated user outlet = %+v", userResponse)
	c.JSON(http.StatusOK, userResponse)
}

func (h *UserHandler) 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 *UserHandler) 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":     constants.UserValidatorEntity,
		},
	}
	c.JSON(statusCode, errorResponse)
}
