package services

import (
	"actatempus_backend/internal/application/services/dto"
	mappers "actatempus_backend/internal/application/services/mapper"
	"actatempus_backend/internal/domain/repository"
	"net/http"

	A "github.com/IBM/fp-go/array"
	E "github.com/IBM/fp-go/either"
	F "github.com/IBM/fp-go/function"
	"github.com/gin-gonic/gin"
)

// UserService handles user-related HTTP requests.
type UserService struct {
	authRepository repository.AuthRepository
	repository     repository.UserRepository
}

// NewUserService creates a new instance of UserService.
func NewUserService(authRepo repository.AuthRepository, repo repository.UserRepository) *UserService {
	return &UserService{authRepository: authRepo, repository: repo}
}

// RegisterRoutes registers the user-related routes with Gin.
func (s *UserService) RegisterRoutes(router *gin.RouterGroup) {
	router.POST("/", s.CreateUser)
	router.GET("/:id", s.GetUserByID)
	router.GET("/", s.GetAllUsers)
	router.PUT("/:id", s.UpdateUser)
	router.DELETE("/:id", s.DeleteUser)
}

// CreateUser handles the creation of a new user.
func (s *UserService) CreateUser(c *gin.Context) {
	var userCreateDTO dto.UserCreateDTO
	if err := c.ShouldBindJSON(&userCreateDTO); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	F.Pipe3(
		mappers.MapCreateDTOToUser(userCreateDTO),
		s.repository.Create(c.Request.Context()),
		E.Map[error](mappers.MapUserToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.UserDTO](c, http.StatusCreated),
		),
	)

	//F.Pipe5( // disabled for creating the initial user
	//	CheckAuth(c, s.authRepository),
	//	E.Map[error](F.Constant1[string](userCreateDTO)),
	//	E.Map[error](mappers.MapCreateDTOToUser),
	//	E.Chain(
	//		s.repository.Create(c.Request.Context()),
	//	),
	//	E.Map[error](mappers.MapUserToDTO),
	//	E.Fold(
	//		HandleError(c),
	//		HandleSuccess[dto.UserDTO](c, http.StatusCreated),
	//	),
	//)
}

// GetUserByID handles fetching a user by their ID.
func (s *UserService) GetUserByID(c *gin.Context) {
	F.Pipe4(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](c.Param("id"))),
		E.Chain(s.repository.FindByID(c.Request.Context())),
		E.Map[error](mappers.MapUserToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.UserDTO](c, http.StatusOK),
		),
	)
}

// GetAllUsers handles fetching all users.
func (s *UserService) GetAllUsers(c *gin.Context) {
	F.Pipe3(
		CheckAuth(c, s.authRepository),
		E.Chain(F.Constant1[string](s.repository.FindAll(c.Request.Context()))),
		E.Map[error](A.Map(mappers.MapUserToDTO)),
		E.Fold(
			HandleError(c),
			HandleSuccess[[]dto.UserDTO](c, http.StatusOK),
		),
	)
}

// UpdateUser handles updating an existing user.
func (s *UserService) UpdateUser(c *gin.Context) {
	var userUpdateDTO dto.UserUpdateDTO
	if err := c.ShouldBindJSON(&userUpdateDTO); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	F.Pipe5(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](c.Param("id"))),
		E.Map[error](F.Curry2(mappers.MapUpdateDTOToUser)(userUpdateDTO)),
		E.Chain(s.repository.Update(c.Request.Context())),
		E.Map[error](mappers.MapUserToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.UserDTO](c, http.StatusOK),
		),
	)
}

// DeleteUser handles deleting a user by their ID.
func (s *UserService) DeleteUser(c *gin.Context) {
	F.Pipe4(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](c.Param("id"))),
		E.Chain(s.repository.Delete(c.Request.Context())),
		E.Map[error](mappers.MapUserToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.UserDTO](c, http.StatusOK),
		),
	)
}
