1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package event
  6
  7import (
  8	"time"
  9
 10	"git.secluded.site/np/internal/goal"
 11	"git.secluded.site/np/internal/task"
 12	"git.secluded.site/np/internal/timeutil"
 13)
 14
 15// GoalSnapshot captures relevant goal fields for event payloads.
 16type GoalSnapshot struct {
 17	Title       string    `json:"title"`
 18	Description string    `json:"description"`
 19	UpdatedAt   time.Time `json:"updated_at"`
 20}
 21
 22// GoalSetPayload records a goal being established.
 23type GoalSetPayload struct {
 24	Goal GoalSnapshot `json:"goal"`
 25}
 26
 27// GoalUpdatedPayload records a goal changing.
 28type GoalUpdatedPayload struct {
 29	Before GoalSnapshot `json:"goal_before"`
 30	After  GoalSnapshot `json:"goal_after"`
 31}
 32
 33// TaskAddedPayload records a newly added task.
 34type TaskAddedPayload struct {
 35	Task task.Task `json:"task"`
 36}
 37
 38// TaskStateSnapshot captures mutable task fields for updates.
 39type TaskStateSnapshot struct {
 40	Title       string    `json:"title"`
 41	Description string    `json:"description"`
 42	UpdatedAt   time.Time `json:"updated_at"`
 43}
 44
 45// TaskUpdatedPayload captures title/description edits.
 46type TaskUpdatedPayload struct {
 47	TaskID string            `json:"task_id"`
 48	Before TaskStateSnapshot `json:"before"`
 49	After  TaskStateSnapshot `json:"after"`
 50}
 51
 52// TaskStatusChangedPayload captures status transitions.
 53type TaskStatusChangedPayload struct {
 54	TaskID       string      `json:"task_id"`
 55	Title        string      `json:"title"`
 56	StatusBefore task.Status `json:"status_before"`
 57	StatusAfter  task.Status `json:"status_after"`
 58	UpdatedAt    time.Time   `json:"updated_at"`
 59}
 60
 61// GoalSnapshotFrom converts a goal document into an event snapshot.
 62func GoalSnapshotFrom(doc goal.Document) GoalSnapshot {
 63	return GoalSnapshot{
 64		Title:       doc.Title,
 65		Description: doc.Description,
 66		UpdatedAt:   timeutil.EnsureUTC(doc.UpdatedAt),
 67	}
 68}
 69
 70// TaskStateSnapshotFrom converts a task into a mutable-field snapshot.
 71func TaskStateSnapshotFrom(t task.Task) TaskStateSnapshot {
 72	return TaskStateSnapshot{
 73		Title:       t.Title,
 74		Description: t.Description,
 75		UpdatedAt:   timeutil.EnsureUTC(t.UpdatedAt),
 76	}
 77}
 78
 79// BuildGoalSet constructs AppendInput for goal_set events.
 80func BuildGoalSet(command, reason string, doc goal.Document) AppendInput {
 81	return AppendInput{
 82		Type:    TypeGoalSet,
 83		Command: command,
 84		Reason:  reason,
 85		Payload: GoalSetPayload{
 86			Goal: GoalSnapshotFrom(doc),
 87		},
 88	}
 89}
 90
 91// BuildGoalUpdated constructs AppendInput for goal_updated events.
 92func BuildGoalUpdated(command, reason string, before, after goal.Document) AppendInput {
 93	return AppendInput{
 94		Type:    TypeGoalUpdated,
 95		Command: command,
 96		Reason:  reason,
 97		Payload: GoalUpdatedPayload{
 98			Before: GoalSnapshotFrom(before),
 99			After:  GoalSnapshotFrom(after),
100		},
101	}
102}
103
104// BuildTaskAdded constructs AppendInput for task_added events.
105func BuildTaskAdded(command, reason string, t task.Task) AppendInput {
106	return AppendInput{
107		Type:    TypeTaskAdded,
108		Command: command,
109		Reason:  reason,
110		Payload: TaskAddedPayload{
111			Task: t,
112		},
113	}
114}
115
116// BuildTaskUpdated constructs AppendInput for task_updated events.
117func BuildTaskUpdated(command, reason, taskID string, before, after task.Task) AppendInput {
118	return AppendInput{
119		Type:    TypeTaskUpdated,
120		Command: command,
121		Reason:  reason,
122		Payload: TaskUpdatedPayload{
123			TaskID: taskID,
124			Before: TaskStateSnapshotFrom(before),
125			After:  TaskStateSnapshotFrom(after),
126		},
127	}
128}
129
130// BuildTaskStatusChanged constructs AppendInput for task_status_changed events.
131func BuildTaskStatusChanged(command, reason string, before task.Task, after task.Task) AppendInput {
132	return AppendInput{
133		Type:    TypeTaskStatusChange,
134		Command: command,
135		Reason:  reason,
136		Payload: TaskStatusChangedPayload{
137			TaskID:       after.ID,
138			Title:        after.Title,
139			StatusBefore: before.Status,
140			StatusAfter:  after.Status,
141			UpdatedAt:    timeutil.EnsureUTC(after.UpdatedAt),
142		},
143	}
144}