1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5// Package stats provides helpers for computing task statistics.
6package stats
7
8import (
9 "context"
10 "fmt"
11
12 "git.secluded.site/go-lunatask"
13 "git.secluded.site/lune/internal/ui"
14)
15
16// TaskCounter provides methods to count tasks by various criteria.
17type TaskCounter struct {
18 client *lunatask.Client
19 tasks []lunatask.Task
20}
21
22// NewTaskCounter creates a counter that fetches tasks on first use.
23func NewTaskCounter(client *lunatask.Client) *TaskCounter {
24 return &TaskCounter{client: client}
25}
26
27// UncompletedInArea counts uncompleted tasks in the specified area.
28func (tc *TaskCounter) UncompletedInArea(ctx context.Context, areaID string) (int, error) {
29 if err := tc.ensureTasks(ctx); err != nil {
30 return 0, err
31 }
32
33 count := 0
34
35 for _, task := range tc.tasks {
36 if !isUncompleted(task) {
37 continue
38 }
39
40 if task.AreaID != nil && *task.AreaID == areaID {
41 count++
42 }
43 }
44
45 return count, nil
46}
47
48// UncompletedInGoal counts uncompleted tasks in the specified goal.
49func (tc *TaskCounter) UncompletedInGoal(ctx context.Context, goalID string) (int, error) {
50 if err := tc.ensureTasks(ctx); err != nil {
51 return 0, err
52 }
53
54 count := 0
55
56 for _, task := range tc.tasks {
57 if !isUncompleted(task) {
58 continue
59 }
60
61 if task.GoalID != nil && *task.GoalID == goalID {
62 count++
63 }
64 }
65
66 return count, nil
67}
68
69// ensureTasks fetches tasks if not already loaded.
70func (tc *TaskCounter) ensureTasks(ctx context.Context) error {
71 if tc.tasks != nil {
72 return nil
73 }
74
75 tasks, err := ui.Spin("Fetching tasks…", func() ([]lunatask.Task, error) {
76 return tc.client.ListTasks(ctx, nil)
77 })
78 if err != nil {
79 return fmt.Errorf("listing tasks: %w", err)
80 }
81
82 tc.tasks = tasks
83
84 return nil
85}
86
87func isUncompleted(task lunatask.Task) bool {
88 if task.Status == nil {
89 return true
90 }
91
92 return *task.Status != lunatask.StatusCompleted
93}