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

package lunatask_test

import (
	"slices"
	"testing"

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

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

	tests := []struct {
		name    string
		input   string
		want    lunatask.Workflow
		wantErr bool
	}{
		{"priority_list_snake", "priority_list", lunatask.WorkflowPriorityList, false},
		{"priority_list_kebab", "priority-list", lunatask.WorkflowPriorityList, false},
		{"priority_list_upper", "PRIORITY_LIST", lunatask.WorkflowPriorityList, false},
		{"priority_list_mixed", "Priority-List", lunatask.WorkflowPriorityList, false},
		{"now_later_snake", "now_later", lunatask.WorkflowNowLater, false},
		{"now_later_kebab", "now-later", lunatask.WorkflowNowLater, false},
		{"now_later_upper", "NOW_LATER", lunatask.WorkflowNowLater, false},
		{"kanban_lower", "kanban", lunatask.WorkflowKanban, false},
		{"kanban_upper", "KANBAN", lunatask.WorkflowKanban, false},
		{"plan_your_days_snake", "plan_your_days", lunatask.WorkflowPlanYourDays, false},
		{"plan_your_days_kebab", "plan-your-days", lunatask.WorkflowPlanYourDays, false},
		{"must_should_want_snake", "must_should_want", lunatask.WorkflowMustShouldWant, false},
		{"must_should_want_kebab", "must-should-want", lunatask.WorkflowMustShouldWant, false},
		{"eisenhower_lower", "eisenhower", lunatask.WorkflowEisenhower, false},
		{"eisenhower_upper", "EISENHOWER", lunatask.WorkflowEisenhower, false},
		{"invalid", "invalid", "", true},
		{"empty", "", "", true},
		{"numeric", "1", "", true},
		{"typo", "kanbann", "", true},
	}

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

			got, err := lunatask.ParseWorkflow(testCase.input)
			if (err != nil) != testCase.wantErr {
				t.Errorf("ParseWorkflow(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)

				return
			}

			if !testCase.wantErr && got != testCase.want {
				t.Errorf("ParseWorkflow(%q) = %q, want %q", testCase.input, got, testCase.want)
			}
		})
	}
}

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

	tests := []struct {
		name  string
		value lunatask.Workflow
		want  string
	}{
		{"priority_list", lunatask.WorkflowPriorityList, "priority_list"},
		{"now_later", lunatask.WorkflowNowLater, "now_later"},
		{"kanban", lunatask.WorkflowKanban, "kanban"},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, "plan_your_days"},
		{"must_should_want", lunatask.WorkflowMustShouldWant, "must_should_want"},
		{"eisenhower", lunatask.WorkflowEisenhower, "eisenhower"},
	}

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

			if got := tc.value.String(); got != tc.want {
				t.Errorf("Workflow.String() = %q, want %q", got, tc.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		want     []lunatask.TaskStatus
	}{
		{
			"priority_list",
			lunatask.WorkflowPriorityList,
			nil,
		},
		{
			"now_later",
			lunatask.WorkflowNowLater,
			[]lunatask.TaskStatus{lunatask.StatusLater, lunatask.StatusInProgress, lunatask.StatusCompleted},
		},
		{
			"kanban",
			lunatask.WorkflowKanban,
			[]lunatask.TaskStatus{
				lunatask.StatusLater, lunatask.StatusNext, lunatask.StatusInProgress,
				lunatask.StatusWaiting, lunatask.StatusCompleted,
			},
		},
		{
			"plan_your_days",
			lunatask.WorkflowPlanYourDays,
			[]lunatask.TaskStatus{lunatask.StatusCompleted},
		},
		{
			"must_should_want",
			lunatask.WorkflowMustShouldWant,
			[]lunatask.TaskStatus{lunatask.StatusCompleted},
		},
		{
			"eisenhower",
			lunatask.WorkflowEisenhower,
			[]lunatask.TaskStatus{lunatask.StatusCompleted},
		},
		{
			"unknown",
			lunatask.Workflow("unknown"),
			nil,
		},
	}

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

			got := testCase.workflow.ValidStatuses()
			if !slices.Equal(got, testCase.want) {
				t.Errorf("Workflow.ValidStatuses() = %v, want %v", got, testCase.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		want     bool
	}{
		{"priority_list", lunatask.WorkflowPriorityList, false},
		{"now_later", lunatask.WorkflowNowLater, false},
		{"kanban", lunatask.WorkflowKanban, false},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
		{"must_should_want", lunatask.WorkflowMustShouldWant, true},
		{"eisenhower", lunatask.WorkflowEisenhower, false},
	}

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

			if got := tc.workflow.UsesMotivation(); got != tc.want {
				t.Errorf("Workflow.UsesMotivation() = %v, want %v", got, tc.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		want     bool
	}{
		{"priority_list", lunatask.WorkflowPriorityList, false},
		{"now_later", lunatask.WorkflowNowLater, false},
		{"kanban", lunatask.WorkflowKanban, false},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
		{"eisenhower", lunatask.WorkflowEisenhower, true},
	}

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

			if got := tc.workflow.UsesEisenhower(); got != tc.want {
				t.Errorf("Workflow.UsesEisenhower() = %v, want %v", got, tc.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		want     bool
	}{
		{"priority_list", lunatask.WorkflowPriorityList, false},
		{"now_later", lunatask.WorkflowNowLater, false},
		{"kanban", lunatask.WorkflowKanban, false},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, true},
		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
		{"eisenhower", lunatask.WorkflowEisenhower, false},
	}

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

			if got := tc.workflow.UsesScheduling(); got != tc.want {
				t.Errorf("Workflow.UsesScheduling() = %v, want %v", got, tc.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		want     bool
	}{
		{"priority_list", lunatask.WorkflowPriorityList, true},
		{"now_later", lunatask.WorkflowNowLater, false},
		{"kanban", lunatask.WorkflowKanban, false},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
		{"eisenhower", lunatask.WorkflowEisenhower, false},
	}

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

			if got := tc.workflow.UsesPriority(); got != tc.want {
				t.Errorf("Workflow.UsesPriority() = %v, want %v", got, tc.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		workflow lunatask.Workflow
		contains string
	}{
		{"priority_list", lunatask.WorkflowPriorityList, "priority"},
		{"now_later", lunatask.WorkflowNowLater, "in-progress"},
		{"kanban", lunatask.WorkflowKanban, "kanban"},
		{"plan_your_days", lunatask.WorkflowPlanYourDays, "scheduled_on"},
		{"must_should_want", lunatask.WorkflowMustShouldWant, "must"},
		{"eisenhower", lunatask.WorkflowEisenhower, "Eisenhower"},
		{"unknown", lunatask.Workflow("unknown"), "Unknown"},
	}

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

			got := testCase.workflow.Description()
			if got == "" {
				t.Error("Workflow.Description() returned empty string")
			}

			if !contains(got, testCase.contains) {
				t.Errorf("Workflow.Description() = %q, want to contain %q", got, testCase.contains)
			}
		})
	}
}

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

	workflows := lunatask.Workflows()

	if len(workflows) != 6 {
		t.Errorf("Workflows() returned %d workflows, want 6", len(workflows))
	}

	expected := []lunatask.Workflow{
		lunatask.WorkflowPriorityList,
		lunatask.WorkflowNowLater,
		lunatask.WorkflowKanban,
		lunatask.WorkflowPlanYourDays,
		lunatask.WorkflowMustShouldWant,
		lunatask.WorkflowEisenhower,
	}

	if !slices.Equal(workflows, expected) {
		t.Errorf("Workflows() = %v, want %v", workflows, expected)
	}
}

func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
		(len(s) > 0 && len(substr) > 0 && findSubstring(s, substr)))
}

func findSubstring(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}

	return false
}
