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

// ProjectTaskService handles project task-related HTTP requests.
type ProjectTaskService struct {
	authRepository repository.AuthRepository
	repository     repository.ProjectTaskRepository
}

// NewProjectTaskService creates a new instance of ProjectTaskService.
func NewProjectTaskService(authRepo repository.AuthRepository, repo repository.ProjectTaskRepository) *ProjectTaskService {
	return &ProjectTaskService{authRepository: authRepo, repository: repo}
}

// RegisterRoutes registers the project task-related routes with Gin.
func (s *ProjectTaskService) RegisterRoutes(router *gin.RouterGroup) {
	router.POST("/", s.CreateTask)
	router.GET("/:id", s.GetTaskByID)
	router.GET("/", s.GetAllTasks)
	router.GET("/project/:projectID", s.GetTasksByProjectID)
	router.PUT("/:id", s.UpdateTask)
	router.DELETE("/:id", s.DeleteTask)
}

// CreateTask handles the creation of a new project task.
func (s *ProjectTaskService) CreateTask(c *gin.Context) {
	var taskCreateDTO dto.ProjectTaskCreateDTO
	if err := c.ShouldBindJSON(&taskCreateDTO); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	F.Pipe5(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](taskCreateDTO)),
		E.Map[error](mappers.MapCreateDTOToProjectTask),
		E.Chain(
			s.repository.Create(c.Request.Context()),
		),
		E.Map[error](mappers.MapProjectTaskToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectTaskDTO](c, http.StatusCreated),
		),
	)
}

// GetTaskByID handles fetching a project task by its ID.
func (s *ProjectTaskService) GetTaskByID(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.MapProjectTaskToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectTaskDTO](c, http.StatusOK),
		),
	)
}

// GetTasksByProjectID handles fetching project tasks by project ID.
func (s *ProjectTaskService) GetTasksByProjectID(c *gin.Context) {
	F.Pipe4(
		CheckAuth(c, s.authRepository),
		E.Map[error](F.Constant1[string](c.Param("projectID"))),
		E.Chain(s.repository.FindByProjectID(c.Request.Context())),
		E.Map[error](A.Map(mappers.MapProjectTaskToDTO)),
		E.Fold(
			HandleError(c),
			HandleSuccess[[]dto.ProjectTaskDTO](c, http.StatusOK),
		),
	)
}

// GetAllTasks handles fetching all project tasks.
func (s *ProjectTaskService) GetAllTasks(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.MapProjectTaskToDTO)),
		E.Fold(
			HandleError(c),
			HandleSuccess[[]dto.ProjectTaskDTO](c, http.StatusOK),
		),
	)
}

// UpdateTask handles updating an existing project task.
func (s *ProjectTaskService) UpdateTask(c *gin.Context) {
	var taskUpdateDTO dto.ProjectTaskUpdateDTO

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

// DeleteTask handles deleting a project task by its ID and returns the deleted object.
func (s *ProjectTaskService) DeleteTask(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.MapProjectTaskToDTO),
		E.Fold(
			HandleError(c),
			HandleSuccess[dto.ProjectTaskDTO](c, http.StatusOK),
		),
	)
}
