package checkvist import ( "context" "fmt" "time" ) // tasks.go contains the TaskService for CRUD operations on tasks within a checklist. // TaskService provides operations on tasks within a specific checklist. type TaskService struct { client *Client checklistID int } // Tasks returns a TaskService for performing task operations on the specified checklist. func (c *Client) Tasks(checklistID int) *TaskService { return &TaskService{ client: c, checklistID: checklistID, } } // List returns all tasks in the checklist. func (s *TaskService) List(ctx context.Context) ([]Task, error) { path := fmt.Sprintf("/checklists/%d/tasks.json", s.checklistID) var tasks []Task if err := s.client.doGet(ctx, path, &tasks); err != nil { return nil, err } // Parse due dates for i := range tasks { parseDueDate(&tasks[i]) } return tasks, nil } // Get returns a single task by ID, including its parent hierarchy. func (s *TaskService) Get(ctx context.Context, taskID int) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID) var task Task if err := s.client.doGet(ctx, path, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // CreateTaskRequest represents the request body for creating a task. type CreateTaskRequest struct { Content string `json:"content"` ParentID int `json:"parent_id,omitempty"` Position int `json:"position,omitempty"` Due string `json:"due,omitempty"` Priority int `json:"priority,omitempty"` Tags string `json:"tags,omitempty"` } // TaskBuilder provides a fluent interface for building task creation requests. type TaskBuilder struct { content string parentID int position int due string priority int tags []string } // NewTask creates a new TaskBuilder with the given content. func NewTask(content string) *TaskBuilder { return &TaskBuilder{content: content} } // WithParent sets the parent task ID for creating a subtask. func (b *TaskBuilder) WithParent(parentID int) *TaskBuilder { b.parentID = parentID return b } // WithPosition sets the position of the task within its siblings. func (b *TaskBuilder) WithPosition(position int) *TaskBuilder { b.position = position return b } // WithDueDate sets the due date using a DueDate value. func (b *TaskBuilder) WithDueDate(due DueDate) *TaskBuilder { b.due = due.String() return b } // WithPriority sets the priority level (1 = highest, 2 = high, 0 = normal). func (b *TaskBuilder) WithPriority(priority int) *TaskBuilder { b.priority = priority return b } // WithTags sets the tags for the task. func (b *TaskBuilder) WithTags(tags ...string) *TaskBuilder { b.tags = tags return b } // build converts the TaskBuilder to a CreateTaskRequest. func (b *TaskBuilder) build() CreateTaskRequest { req := CreateTaskRequest{ Content: b.content, ParentID: b.parentID, Position: b.position, Due: b.due, Priority: b.priority, } if len(b.tags) > 0 { for i, tag := range b.tags { if i > 0 { req.Tags += ", " } req.Tags += tag } } return req } // Create creates a new task using a TaskBuilder. func (s *TaskService) Create(ctx context.Context, builder *TaskBuilder) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks.json", s.checklistID) body := builder.build() var task Task if err := s.client.doPost(ctx, path, body, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // UpdateTaskRequest represents the request body for updating a task. type UpdateTaskRequest struct { Content *string `json:"content,omitempty"` ParentID *int `json:"parent_id,omitempty"` Position *int `json:"position,omitempty"` Due *string `json:"due,omitempty"` Priority *int `json:"priority,omitempty"` Tags *string `json:"tags,omitempty"` } // Update updates an existing task. func (s *TaskService) Update(ctx context.Context, taskID int, req UpdateTaskRequest) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID) var task Task if err := s.client.doPut(ctx, path, req, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // Delete permanently deletes a task. func (s *TaskService) Delete(ctx context.Context, taskID int) error { path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID) return s.client.doDelete(ctx, path) } // Close marks a task as completed. func (s *TaskService) Close(ctx context.Context, taskID int) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks/%d/close.json", s.checklistID, taskID) var task Task if err := s.client.doPost(ctx, path, nil, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // Reopen reopens a closed or invalidated task. func (s *TaskService) Reopen(ctx context.Context, taskID int) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks/%d/reopen.json", s.checklistID, taskID) var task Task if err := s.client.doPost(ctx, path, nil, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // Invalidate marks a task as invalidated (strikethrough). func (s *TaskService) Invalidate(ctx context.Context, taskID int) (*Task, error) { path := fmt.Sprintf("/checklists/%d/tasks/%d/invalidate.json", s.checklistID, taskID) var task Task if err := s.client.doPost(ctx, path, nil, &task); err != nil { return nil, err } parseDueDate(&task) return &task, nil } // parseDueDate attempts to parse the DueDateRaw string into a time.Time. // It supports ISO 8601 date format (YYYY-MM-DD). func parseDueDate(task *Task) { if task.DueDateRaw == "" { return } // Try to parse as ISO date if t, err := time.Parse("2006-01-02", task.DueDateRaw); err == nil { task.DueDate = &t } }