package handlers

import (
	"context"
	"fmt"

	"github.com/gin-gonic/gin"
	"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"
)

// TimeEntryHandler handles time entry-related API endpoints
type TimeEntryHandler struct{}

// NewTimeEntryHandler creates a new TimeEntryHandler
func NewTimeEntryHandler() *TimeEntryHandler {
	return &TimeEntryHandler{}
}

// GetTimeEntries handles GET /time-entries
//
//	@Summary		Get all time entries
//	@Description	Get a list of all time entries
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Success		200	{object}	utils.Response{data=[]dto.TimeEntryDto}
//	@Failure		401	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500	{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/time-entries [get]
func (h *TimeEntryHandler) GetTimeEntries(c *gin.Context) {
	utils.HandleGetAll(c, models.GetAllTimeEntries, convertTimeEntryToDTO, "time entries")
}

// GetTimeEntryByID handles GET /time-entries/:id
//
//	@Summary		Get time entry by ID
//	@Description	Get a time entry by its ID
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id	path		string	true	"Time Entry ID"
//	@Success		200	{object}	utils.Response{data=dto.TimeEntryDto}
//	@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			/time-entries/{id} [get]
func (h *TimeEntryHandler) GetTimeEntryByID(c *gin.Context) {
	utils.HandleGetByID(c, models.GetTimeEntryByID, convertTimeEntryToDTO, "time entry")
}

// GetTimeEntriesByUserID handles GET /time-entries/user/:userId
//
//	@Summary		Get time entries by user ID
//	@Description	Get a list of time entries for a specific user
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			userId	path		string	true	"User ID"
//	@Success		200		{object}	utils.Response{data=[]dto.TimeEntryDto}
//	@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			/time-entries/user/{userId} [get]
func (h *TimeEntryHandler) GetTimeEntriesByUserID(c *gin.Context) {
	utils.HandleGetByFilter(c, models.GetTimeEntriesByUserID, convertTimeEntryToDTO, "time entries", "userId")
}

// GetMyTimeEntries handles GET /time-entries/me
//
//	@Summary		Get current user's time entries
//	@Description	Get a list of time entries for the currently authenticated user
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Success		200	{object}	utils.Response{data=[]dto.TimeEntryDto}
//	@Failure		401	{object}	utils.Response{error=utils.ErrorInfo}
//	@Failure		500	{object}	utils.Response{error=utils.ErrorInfo}
//	@Router			/time-entries/me [get]
func (h *TimeEntryHandler) GetMyTimeEntries(c *gin.Context) {
	utils.HandleGetByUserID(c, models.GetTimeEntriesByUserID, convertTimeEntryToDTO, "time entries")
}

// GetTimeEntriesByProjectID handles GET /time-entries/project/:projectId
//
//	@Summary		Get time entries by project ID
//	@Description	Get a list of time entries for a specific project
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			projectId	path		string	true	"Project ID"
//	@Success		200			{object}	utils.Response{data=[]dto.TimeEntryDto}
//	@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			/time-entries/project/{projectId} [get]
func (h *TimeEntryHandler) GetTimeEntriesByProjectID(c *gin.Context) {
	utils.HandleGetByFilter(c, models.GetTimeEntriesByProjectID, convertTimeEntryToDTO, "time entries", "projectId")
}

// GetTimeEntriesByDateRange handles GET /time-entries/range
//
//	@Summary		Get time entries by date range
//	@Description	Get a list of time entries within a specific date range
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			start	query		string	true	"Start date (ISO 8601 format)"
//	@Param			end		query		string	true	"End date (ISO 8601 format)"
//	@Success		200		{object}	utils.Response{data=[]dto.TimeEntryDto}
//	@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			/time-entries/range [get]
func (h *TimeEntryHandler) GetTimeEntriesByDateRange(c *gin.Context) {
	utils.HandleGetByDateRange(c, models.GetTimeEntriesByDateRange, convertTimeEntryToDTO, "time entries")
}

// CreateTimeEntry handles POST /time-entries
//
//	@Summary		Create a new time entry
//	@Description	Create a new time entry
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			timeEntry	body		dto.TimeEntryCreateDto	true	"Time Entry data"
//	@Success		201			{object}	utils.Response{data=dto.TimeEntryDto}
//	@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			/time-entries [post]
func (h *TimeEntryHandler) CreateTimeEntry(c *gin.Context) {
	utils.HandleCreate(c, createTimeEntryWrapper, convertTimeEntryToDTO, "time entry")
}

// UpdateTimeEntry handles PUT /time-entries/:id
//
//	@Summary		Update a time entry
//	@Description	Update an existing time entry
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id			path		string					true	"Time Entry ID"
//	@Param			timeEntry	body		dto.TimeEntryUpdateDto	true	"Time Entry data"
//	@Success		200			{object}	utils.Response{data=dto.TimeEntryDto}
//	@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			/time-entries/{id} [put]
func (h *TimeEntryHandler) UpdateTimeEntry(c *gin.Context) {
	utils.HandleUpdate(c, models.UpdateTimeEntry, convertTimeEntryToDTO, prepareTimeEntryUpdate, "time entry")
}

// DeleteTimeEntry handles DELETE /time-entries/:id
//
//	@Summary		Delete a time entry
//	@Description	Delete a time entry by its ID
//	@Tags			time-entries
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			id	path		string	true	"Time Entry 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			/time-entries/{id} [delete]
func (h *TimeEntryHandler) DeleteTimeEntry(c *gin.Context) {
	utils.HandleDelete(c, models.DeleteTimeEntry, "time entry")
}

// Helper functions for DTO conversion

func convertTimeEntryToDTO(timeEntry *models.TimeEntry) dto.TimeEntryDto {
	return dto.TimeEntryDto{
		ID:          timeEntry.ID.String(),
		CreatedAt:   timeEntry.CreatedAt,
		UpdatedAt:   timeEntry.UpdatedAt,
		UserID:      timeEntry.UserID.String(),     // Simplified conversion
		ProjectID:   timeEntry.ProjectID.String(),  // Simplified conversion
		ActivityID:  timeEntry.ActivityID.String(), // Simplified conversion
		Start:       timeEntry.Start,
		End:         timeEntry.End,
		Description: timeEntry.Description,
		Billable:    timeEntry.Billable,
	}
}

func convertCreateTimeEntryDTOToModel(dto dto.TimeEntryCreateDto) (models.TimeEntryCreate, error) {
	// Convert IDs from int to ULID (this is a simplification, adjust as needed)
	userID, err := types.ULIDFromString(dto.UserID)
	if err != nil {
		return models.TimeEntryCreate{}, fmt.Errorf("invalid user ID: %w", err)
	}

	projectID, err := types.ULIDFromString(dto.ProjectID)
	if err != nil {
		return models.TimeEntryCreate{}, fmt.Errorf("invalid project ID: %w", err)
	}

	activityID, err := types.ULIDFromString(dto.ActivityID)
	if err != nil {
		return models.TimeEntryCreate{}, fmt.Errorf("invalid activity ID: %w", err)
	}

	return models.TimeEntryCreate{
		UserID:      userID,
		ProjectID:   projectID,
		ActivityID:  activityID,
		Start:       dto.Start,
		End:         dto.End,
		Description: dto.Description,
		Billable:    dto.Billable,
	}, nil
}

func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto, id types.ULID) (models.TimeEntryUpdate, error) {

	update := models.TimeEntryUpdate{
		ID: id,
	}

	if dto.UserID != nil {
		userID, err := types.ULIDFromString(*dto.UserID)
		if err != nil {
			return models.TimeEntryUpdate{}, fmt.Errorf("invalid user ID: %w", err)
		}
		update.UserID = &userID
	}

	if dto.ProjectID != nil {
		projectID, err := types.ULIDFromString(*dto.ProjectID)
		if err != nil {
			return models.TimeEntryUpdate{}, fmt.Errorf("invalid project ID: %w", err)
		}
		update.ProjectID = &projectID
	}

	if dto.ActivityID != nil {
		activityID, err := types.ULIDFromString(*dto.ActivityID)
		if err != nil {
			return models.TimeEntryUpdate{}, fmt.Errorf("invalid activity ID: %w", err)
		}
		update.ActivityID = &activityID
	}

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

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

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

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

	return update, nil
}

// prepareTimeEntryUpdate prepares the time entry update object by parsing the ID, binding the JSON, and converting the DTO to a model
func prepareTimeEntryUpdate(c *gin.Context) (models.TimeEntryUpdate, error) {
	// Parse ID from URL
	idStr := c.Param("id")
	id, err := types.ULIDFromString(idStr)
	if err != nil {
		responses.BadRequestResponse(c, "Invalid time entry ID format")
		return models.TimeEntryUpdate{}, err
	}

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

	// Convert DTO to model
	timeEntryUpdate, err := convertUpdateTimeEntryDTOToModel(timeEntryUpdateDTO, id)
	if err != nil {
		responses.BadRequestResponse(c, err.Error())
		return models.TimeEntryUpdate{}, err
	}

	return timeEntryUpdate, nil
}

// createTimeEntryWrapper is a wrapper function for models.CreateTimeEntry that takes a DTO as input
func createTimeEntryWrapper(ctx context.Context, createDTO dto.TimeEntryCreateDto) (*models.TimeEntry, error) {
	// Convert DTO to model
	timeEntryCreate, err := convertCreateTimeEntryDTOToModel(createDTO)
	if err != nil {
		return nil, err
	}

	// Call the original function
	return models.CreateTimeEntry(ctx, timeEntryCreate)
}
