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"
)

// ProjectService handles project-related HTTP requests.
type ProjectService struct {
	authRepository repository.AuthRepository
	repository     repository.ProjectRepository
}

// NewProjectService creates a new instance of ProjectService.
func NewProjectService(authRepo repository.AuthRepository, repo repository.ProjectRepository) *ProjectService {
	return &ProjectService{authRepository: authRepo, repository: repo}
}

// RegisterRoutes registers the project-related routes with Gin.
func (s *ProjectService) RegisterRoutes(router *gin.RouterGroup) {
	router.POST("/", s.CreateProject)
	router.GET("/:id", s.GetProjectByID)
	router.GET("/", s.GetAllProjects)
	router.PUT("/:id", s.UpdateProject)
	router.DELETE("/:id", s.DeleteProject)
}

// CreateProject handles the creation of a new project.
func (s *ProjectService) CreateProject(c *gin.Context) {
	var projectCreateDTO dto.ProjectCreateDTO
	if err := c.ShouldBindJSON(&projectCreateDTO); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	F.Pipe5(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](projectCreateDTO)),
		E.Map[error](mappers.MapCreateDTOToProject),
		E.Chain(s.repository.Create(c.Request.Context())),
		E.Map[error](mappers.MapProjectToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectDTO](c, http.StatusCreated),
		),
	)
}

// GetProjectByID handles fetching a project by its ID.
func (s *ProjectService) GetProjectByID(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.MapProjectToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
		),
	)
}

// GetAllProjects handles fetching all projects.
func (s *ProjectService) GetAllProjects(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.MapProjectToDTO)),
		E.Fold(
			HandleError(c),
			HandleSuccess[[]dto.ProjectDTO](c, http.StatusOK),
		),
	)
}

// UpdateProject handles updating an existing project.
func (s *ProjectService) UpdateProject(c *gin.Context) {
	var projectUpdateDTO dto.ProjectUpdateDTO

	if err := c.ShouldBindJSON(&projectUpdateDTO); 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.MapUpdateDTOToProject)(projectUpdateDTO),
		),
		E.Chain(s.repository.Update(c.Request.Context())),
		E.Map[error](mappers.MapProjectToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
		),
	)
}

// DeleteProject handles deleting a project by its ID.
func (s *ProjectService) DeleteProject(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.MapProjectToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
		),
	)
}
