1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package tasks
6
7import (
8 "time"
9
10 "git.secluded.site/go-lunatask"
11)
12
13// FilterFunc filters a slice of tasks.
14type FilterFunc func([]lunatask.Task) []lunatask.Task
15
16// FilterToday returns tasks scheduled for today.
17func FilterToday(tasks []lunatask.Task) []lunatask.Task {
18 today := lunatask.Today()
19 result := make([]lunatask.Task, 0)
20
21 for _, task := range tasks {
22 if task.ScheduledOn != nil && sameDay(task.ScheduledOn.Time, today.Time) {
23 result = append(result, task)
24 }
25 }
26
27 return result
28}
29
30// FilterOverdue returns tasks scheduled before today that are not completed.
31func FilterOverdue(tasks []lunatask.Task) []lunatask.Task {
32 today := lunatask.Today()
33 result := make([]lunatask.Task, 0)
34
35 for _, task := range tasks {
36 if task.ScheduledOn == nil {
37 continue
38 }
39
40 if task.ScheduledOn.Before(today.Time) && !isCompleted(&task) {
41 result = append(result, task)
42 }
43 }
44
45 return result
46}
47
48// Next7DaysWindow is the number of days to look ahead for the next 7 days filter.
49const Next7DaysWindow = 7
50
51// FilterNext7Days returns tasks scheduled within the next 7 days.
52func FilterNext7Days(tasks []lunatask.Task) []lunatask.Task {
53 today := lunatask.Today()
54 weekFromNow := lunatask.NewDate(time.Now().AddDate(0, 0, Next7DaysWindow))
55 result := make([]lunatask.Task, 0)
56
57 for _, task := range tasks {
58 if task.ScheduledOn == nil {
59 continue
60 }
61
62 // Include today through 7 days from now
63 if !task.ScheduledOn.Before(today.Time) && !task.ScheduledOn.After(weekFromNow.Time) {
64 result = append(result, task)
65 }
66 }
67
68 return result
69}
70
71// FilterHighPriority returns tasks with highest priority.
72func FilterHighPriority(tasks []lunatask.Task) []lunatask.Task {
73 result := make([]lunatask.Task, 0)
74
75 for _, task := range tasks {
76 if task.Priority != nil && *task.Priority == lunatask.PriorityHighest {
77 result = append(result, task)
78 }
79 }
80
81 return result
82}
83
84// FilterNow returns tasks that need attention now.
85// Matches tasks where ANY of:
86// - Status = in-progress
87// - Priority = highest
88// - Motivation = "must"
89// - Eisenhower = urgent AND important.
90func FilterNow(tasks []lunatask.Task) []lunatask.Task {
91 result := make([]lunatask.Task, 0)
92
93 for _, task := range tasks {
94 if isNow(&task) {
95 result = append(result, task)
96 }
97 }
98
99 return result
100}
101
102//nolint:cyclop // Multiple independent conditions are inherently complex.
103func isNow(task *lunatask.Task) bool {
104 // Exclude completed tasks
105 if isCompleted(task) {
106 return false
107 }
108
109 // Status = in-progress
110 if task.Status != nil && *task.Status == lunatask.StatusInProgress {
111 return true
112 }
113
114 // Priority = highest
115 if task.Priority != nil && *task.Priority == lunatask.PriorityHighest {
116 return true
117 }
118
119 // Motivation = must
120 if task.Motivation != nil && *task.Motivation == lunatask.MotivationMust {
121 return true
122 }
123
124 // Eisenhower = urgent AND important
125 if task.Eisenhower != nil && task.Eisenhower.IsUrgent() && task.Eisenhower.IsImportant() {
126 return true
127 }
128
129 return false
130}
131
132// RecentCompletionsWindow is the time window for recent completions.
133const RecentCompletionsWindow = 72 * time.Hour
134
135// FilterRecentCompletions returns tasks completed in the last 72 hours.
136func FilterRecentCompletions(tasks []lunatask.Task) []lunatask.Task {
137 cutoff := time.Now().Add(-RecentCompletionsWindow)
138 result := make([]lunatask.Task, 0)
139
140 for _, task := range tasks {
141 if task.CompletedAt != nil && task.CompletedAt.After(cutoff) {
142 result = append(result, task)
143 }
144 }
145
146 return result
147}
148
149// FilterByArea returns tasks in a specific area.
150func FilterByArea(tasks []lunatask.Task, areaID string) []lunatask.Task {
151 result := make([]lunatask.Task, 0)
152
153 for _, task := range tasks {
154 if task.AreaID != nil && *task.AreaID == areaID {
155 result = append(result, task)
156 }
157 }
158
159 return result
160}
161
162func isCompleted(task *lunatask.Task) bool {
163 return task.Status != nil && *task.Status == lunatask.StatusCompleted
164}
165
166func sameDay(a, b time.Time) bool {
167 y1, m1, d1 := a.Date()
168 y2, m2, d2 := b.Date()
169
170 return y1 == y2 && m1 == m2 && d1 == d2
171}