// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package lunatask_test

import (
	"testing"
	"time"

	lunatask "git.secluded.site/go-lunatask"
)

func TestFilterTasks(t *testing.T) {
	t.Parallel()

	today := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
	yesterday := today.Add(-24 * time.Hour)
	tomorrow := today.Add(24 * time.Hour)

	areaA := "area-a"
	areaB := "area-b"
	goalX := "goal-x"
	goalY := "goal-y"

	tasks := []lunatask.Task{
		{ID: "1", AreaID: &areaA, GoalID: &goalX, Status: ptr(lunatask.StatusNext)},
		{ID: "2", AreaID: &areaA, GoalID: &goalY, Status: ptr(lunatask.StatusLater)},
		{ID: "3", AreaID: &areaB, Status: ptr(lunatask.StatusCompleted), CompletedAt: &yesterday},
		{ID: "4", AreaID: &areaB, Status: ptr(lunatask.StatusCompleted), CompletedAt: &tomorrow},
		{ID: "5", Status: ptr(lunatask.StatusInProgress)},
	}

	completed := ptr(lunatask.StatusCompleted)

	tests := []struct {
		name    string
		opts    *lunatask.TaskFilterOptions
		wantIDs []string
	}{
		{"nil_opts_returns_all", nil, []string{"1", "2", "3", "4", "5"}},
		{"empty_opts_excludes_old_completed", &lunatask.TaskFilterOptions{Today: today}, []string{"1", "2", "4", "5"}},
		{"include_completed", &lunatask.TaskFilterOptions{IncludeCompleted: true}, []string{"1", "2", "3", "4", "5"}},
		{"filter_by_area", &lunatask.TaskFilterOptions{AreaID: &areaA, IncludeCompleted: true}, []string{"1", "2"}},
		{"filter_by_goal", &lunatask.TaskFilterOptions{GoalID: &goalX, IncludeCompleted: true}, []string{"1"}},
		{"filter_by_status", &lunatask.TaskFilterOptions{Status: completed, IncludeCompleted: true}, []string{"3", "4"}},
		{"filter_by_status_includes_old", &lunatask.TaskFilterOptions{Status: completed, Today: today}, []string{"3", "4"}},
		{"combined_area_and_status", &lunatask.TaskFilterOptions{AreaID: &areaB, Status: completed}, []string{"3", "4"}},
		{"no_matches", &lunatask.TaskFilterOptions{AreaID: ptr("nonexistent"), IncludeCompleted: true}, []string{}},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()
			assertFilterResult(t, tasks, testCase.opts, testCase.wantIDs)
		})
	}
}

func TestIsOldCompleted(t *testing.T) {
	t.Parallel()

	today := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
	yesterday := today.Add(-24 * time.Hour)
	tomorrow := today.Add(24 * time.Hour)
	completed := ptr(lunatask.StatusCompleted)

	tests := []struct {
		name   string
		task   lunatask.Task
		today  time.Time
		expect bool
	}{
		{"not_completed", lunatask.Task{Status: ptr(lunatask.StatusNext)}, today, false},
		{"nil_status", lunatask.Task{}, today, false},
		{"completed_yesterday", lunatask.Task{Status: completed, CompletedAt: &yesterday}, today, true},
		{"completed_tomorrow", lunatask.Task{Status: completed, CompletedAt: &tomorrow}, today, false},
		{"completed_nil_timestamp", lunatask.Task{Status: completed}, today, true},
		{"completed_exactly_today", lunatask.Task{Status: completed, CompletedAt: &today}, today, false},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			got := lunatask.IsOldCompleted(testCase.task, testCase.today)
			if got != testCase.expect {
				t.Errorf("IsOldCompleted() = %v, want %v", got, testCase.expect)
			}
		})
	}
}

func TestFilterTasks_NilTaskFields(t *testing.T) {
	t.Parallel()

	areaA := "area-a"
	goalX := "goal-x"

	tasks := []lunatask.Task{
		{ID: "1"},
		{ID: "2", AreaID: &areaA},
		{ID: "3", GoalID: &goalX},
	}

	tests := []struct {
		name    string
		opts    *lunatask.TaskFilterOptions
		wantIDs []string
	}{
		{
			"area_filter_skips_nil_area",
			&lunatask.TaskFilterOptions{AreaID: &areaA, IncludeCompleted: true},
			[]string{"2"},
		},
		{
			"goal_filter_skips_nil_goal",
			&lunatask.TaskFilterOptions{GoalID: &goalX, IncludeCompleted: true},
			[]string{"3"},
		},
		{
			"status_filter_skips_nil_status",
			&lunatask.TaskFilterOptions{Status: ptr(lunatask.StatusNext), IncludeCompleted: true},
			[]string{},
		},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()
			assertFilterResult(t, tasks, testCase.opts, testCase.wantIDs)
		})
	}
}

func assertFilterResult(t *testing.T, tasks []lunatask.Task, opts *lunatask.TaskFilterOptions, wantIDs []string) {
	t.Helper()

	got := lunatask.FilterTasks(tasks, opts)
	gotIDs := make([]string, len(got))

	for i, task := range got {
		gotIDs[i] = task.ID
	}

	if len(gotIDs) != len(wantIDs) {
		t.Errorf("FilterTasks() got %v, want %v", gotIDs, wantIDs)

		return
	}

	for i, id := range gotIDs {
		if id != wantIDs[i] {
			t.Errorf("FilterTasks() got %v, want %v", gotIDs, wantIDs)

			return
		}
	}
}
