package handlers

import (
	"context"

	"github.com/gin-gonic/gin"
	"github.com/oklog/ulid/v2"
	"github.com/timetracker/backend/internal/api/dto"
	"github.com/timetracker/backend/internal/api/responses"
	"github.com/timetracker/backend/internal/api/utils"
	"github.com/timetracker/backend/internal/models"
	"github.com/timetracker/backend/internal/types"
)

// ActivityHandler handles activity-related API endpoints
type ActivityHandler struct{}

// NewActivityHandler creates a new ActivityHandler
func NewActivityHandler() *ActivityHandler {
	return &ActivityHandler{}
}

// GetActivities handles GET /activities
//
//	@Summary		Get all activities
//	@Description	Get a list of all activities
//	@Tags			activities
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Success		200	{object}	utils.Response{data=[]dto.ActivityDto}
//	@Failure		401	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500	{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/activities [get]
func (h *ActivityHandler) GetActivities(c *gin.Context) {
	utils.HandleGetAll(c, models.GetAllActivities, convertActivityToDTO, "activities")
}

// GetActivityByID handles GET /activities/:id
//
//	@Summary		Get activity by ID
//	@Description	Get an activity by its ID
//	@Tags			activities
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id	path		string	true	"Activity ID"
//	@Success		200	{object}	utils.Response{data=dto.ActivityDto}
//	@Failure		400	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		401	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		404	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500	{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/activities/{id} [get]
func (h *ActivityHandler) GetActivityByID(c *gin.Context) {
	utils.HandleGetByID(c, models.GetActivityByID, convertActivityToDTO, "activity")
}

// CreateActivity handles POST /activities
//
//	@Summary		Create a new activity
//	@Description	Create a new activity
//	@Tags			activities
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			activity	body		dto.ActivityCreateDto	true	"Activity data"
//	@Success		201			{object}	utils.Response{data=dto.ActivityDto}
//	@Failure		400			{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		401			{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500			{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/activities [post]
func (h *ActivityHandler) CreateActivity(c *gin.Context) {
	utils.HandleCreate(c, createActivityWrapper, convertActivityToDTO, "activity")
}

// UpdateActivity handles PUT /activities/:id
//
//	@Summary		Update an activity
//	@Description	Update an existing activity
//	@Tags			activities
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id			path		string					true	"Activity ID"
//	@Param			activity	body		dto.ActivityUpdateDto	true	"Activity data"
//	@Success		200			{object}	utils.Response{data=dto.ActivityDto}
//	@Failure		400			{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		401			{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		404			{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500			{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/activities/{id} [put]
func (h *ActivityHandler) UpdateActivity(c *gin.Context) {
	utils.HandleUpdate(c, models.UpdateActivity, convertActivityToDTO, prepareActivityUpdate, "activity")
}

// DeleteActivity handles DELETE /activities/:id
//
//	@Summary		Delete an activity
//	@Description	Delete an activity by its ID
//	@Tags			activities
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id	path		string	true	"Activity ID"
//	@Success		204	{object}	utils.Response
//	@Failure		400	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		401	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500	{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/activities/{id} [delete]
func (h *ActivityHandler) DeleteActivity(c *gin.Context) {
	utils.HandleDelete(c, models.DeleteActivity, "activity")
}

// Helper functions for DTO conversion

func convertActivityToDTO(activity *models.Activity) dto.ActivityDto {
	return dto.ActivityDto{
		ID:          activity.ID.String(),
		CreatedAt:   activity.CreatedAt,
		UpdatedAt:   activity.UpdatedAt,
		Name:        activity.Name,
		BillingRate: activity.BillingRate,
	}
}

func convertCreateActivityDTOToModel(dto dto.ActivityCreateDto) models.ActivityCreate {
	return models.ActivityCreate{
		Name:        dto.Name,
		BillingRate: dto.BillingRate,
	}
}

func convertUpdateActivityDTOToModel(dto dto.ActivityUpdateDto) models.ActivityUpdate {
	id, _ := ulid.Parse(dto.ID)
	update := models.ActivityUpdate{
		ID: types.FromULID(id),
	}

	if dto.Name != nil {
		update.Name = dto.Name
	}

	if dto.BillingRate != nil {
		update.BillingRate = dto.BillingRate
	}

	return update
}

// prepareActivityUpdate prepares the activity update object by parsing the ID, binding the JSON, and converting the DTO to a model
func prepareActivityUpdate(c *gin.Context) (models.ActivityUpdate, error) {
	// Parse ID from URL
	id, err := utils.ParseID(c, "id")
	if err != nil {
		responses.BadRequestResponse(c, "Invalid activity ID format")
		return models.ActivityUpdate{}, err
	}

	// Parse request body
	var activityUpdateDTO dto.ActivityUpdateDto
	if err := utils.BindJSON(c, &activityUpdateDTO); err != nil {
		responses.BadRequestResponse(c, err.Error())
		return models.ActivityUpdate{}, err
	}

	// Set ID from URL
	activityUpdateDTO.ID = id.String()

	// Convert DTO to model
	return convertUpdateActivityDTOToModel(activityUpdateDTO), nil
}

// createActivityWrapper is a wrapper function for models.CreateActivity that takes a DTO as input
func createActivityWrapper(ctx context.Context, createDTO dto.ActivityCreateDto) (*models.Activity, error) {
	// Convert DTO to model
	activityCreate := convertCreateActivityDTOToModel(createDTO)

	// Call the original function
	return models.CreateActivity(ctx, activityCreate)
}
