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

//go:build integration

package lunatask_test

import (
	"os"
	"testing"
	"time"

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

var (
	integrationClient *lunatask.Client
	testAreaID        string
)

func TestMain(m *testing.M) {
	apiKey := os.Getenv("LUNATASK_API_KEY")
	if apiKey == "" {
		panic("LUNATASK_API_KEY environment variable required for integration tests")
	}

	testAreaID = os.Getenv("LUNATASK_TEST_AREA")
	if testAreaID == "" {
		panic("LUNATASK_TEST_AREA environment variable required for integration tests")
	}

	integrationClient = lunatask.NewClient(apiKey)

	os.Exit(m.Run())
}

// testName generates a unique name for test entities.
func testName(prefix string) string {
	return prefix + "-" + time.Now().Format("20060102-150405.000")
}

func TestIntegration_Ping(t *testing.T) {
	resp, err := integrationClient.Ping(ctx())
	if err != nil {
		t.Fatalf("Ping() error = %v", err)
	}

	t.Logf("Ping response: %+v", resp)
}

func TestIntegration_ListTasks(t *testing.T) {
	tasks, err := integrationClient.ListTasks(ctx(), nil)
	if err != nil {
		t.Fatalf("ListTasks() error = %v", err)
	}

	t.Logf("Found %d tasks", len(tasks))
}

func TestIntegration_ListNotes(t *testing.T) {
	notes, err := integrationClient.ListNotes(ctx(), nil)
	if err != nil {
		t.Fatalf("ListNotes() error = %v", err)
	}

	t.Logf("Found %d notes", len(notes))
}

func TestIntegration_ListPeople(t *testing.T) {
	people, err := integrationClient.ListPeople(ctx(), nil)
	if err != nil {
		t.Fatalf("ListPeople() error = %v", err)
	}

	t.Logf("Found %d people", len(people))
}

func TestIntegration_TaskRoundTrip(t *testing.T) {
	name := testName("integration-task")

	task, err := integrationClient.NewTask(name).
		InArea(testAreaID).
		WithStatus(lunatask.StatusNext).
		WithEstimate(15).
		Create(ctx())
	if err != nil {
		t.Fatalf("NewTask().Create() error = %v", err)
	}

	if task == nil {
		t.Fatal("NewTask().Create() returned nil (duplicate?)")
	}

	t.Cleanup(func() {
		if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
			t.Errorf("DeleteTask() cleanup error = %v", err)
		}
	})

	t.Logf("Created task: %s", task.ID)

	// Verify we can fetch it
	fetched, err := integrationClient.GetTask(ctx(), task.ID)
	if err != nil {
		t.Fatalf("GetTask() error = %v", err)
	}

	if fetched.ID != task.ID {
		t.Errorf("GetTask().ID = %s, want %s", fetched.ID, task.ID)
	}

	// Update it
	updated, err := integrationClient.NewTaskUpdate(task.ID).
		WithEstimate(30).
		Update(ctx())
	if err != nil {
		t.Fatalf("NewTaskUpdate().Update() error = %v", err)
	}

	if updated.Estimate == nil || *updated.Estimate != 30 {
		t.Errorf("updated estimate = %v, want 30", updated.Estimate)
	}

	t.Logf("Updated task estimate to 30 minutes")
}

func TestIntegration_NoteRoundTrip(t *testing.T) {
	name := testName("integration-note")

	note, err := integrationClient.NewNote().
		WithName(name).
		WithContent("# Test Note\n\nThis is a test.").
		Create(ctx())
	if err != nil {
		t.Fatalf("NewNote().Create() error = %v", err)
	}

	if note == nil {
		t.Fatal("NewNote().Create() returned nil (duplicate?)")
	}

	t.Cleanup(func() {
		if _, err := integrationClient.DeleteNote(ctx(), note.ID); err != nil {
			t.Errorf("DeleteNote() cleanup error = %v", err)
		}
	})

	t.Logf("Created note: %s", note.ID)

	// Verify we can fetch it
	fetched, err := integrationClient.GetNote(ctx(), note.ID)
	if err != nil {
		t.Fatalf("GetNote() error = %v", err)
	}

	if fetched.ID != note.ID {
		t.Errorf("GetNote().ID = %s, want %s", fetched.ID, note.ID)
	}

	// Update it
	updated, err := integrationClient.NewNoteUpdate(note.ID).
		WithContent("# Updated\n\nNew content.").
		Update(ctx())
	if err != nil {
		t.Fatalf("NewNoteUpdate().Update() error = %v", err)
	}

	t.Logf("Updated note: %s", updated.ID)
}

// TestIntegration_TaskWorkflowMetadata tests how the API handles various combinations
// of workflow metadata fields. This helps determine whether lune needs to warn users
// about workflow restrictions.
func TestIntegration_TaskWorkflowMetadata(t *testing.T) {
	t.Run("all_metadata_fields", func(t *testing.T) {
		name := testName("metadata-all")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithStatus(lunatask.StatusNext).
			WithEisenhower(lunatask.EisenhowerDoNow).
			Priority(lunatask.PriorityHigh).
			WithMotivation(lunatask.MotivationMust).
			WithEstimate(60).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		fetched, err := integrationClient.GetTask(ctx(), task.ID)
		if err != nil {
			t.Fatalf("GetTask() error = %v", err)
		}

		logMetadata(t, "all_fields", fetched)
		if fetched.Status == nil || *fetched.Status != lunatask.StatusNext {
			t.Errorf("Status = %v, want %v", fetched.Status, lunatask.StatusNext)
		}
		if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoNow {
			t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoNow)
		}
		if fetched.Priority == nil || *fetched.Priority != lunatask.PriorityHigh {
			t.Errorf("Priority = %v, want %v", fetched.Priority, lunatask.PriorityHigh)
		}
		if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationMust {
			t.Errorf("Motivation = %v, want %v", fetched.Motivation, lunatask.MotivationMust)
		}
	})

	t.Run("eisenhower_only", func(t *testing.T) {
		name := testName("metadata-eisenhower")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithEisenhower(lunatask.EisenhowerDoLater).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		fetched, err := integrationClient.GetTask(ctx(), task.ID)
		if err != nil {
			t.Fatalf("GetTask() error = %v", err)
		}

		logMetadata(t, "eisenhower_only", fetched)
		if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoLater {
			t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoLater)
		}
	})

	t.Run("kanban_status_only", func(t *testing.T) {
		name := testName("metadata-kanban")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithStatus(lunatask.StatusWaiting).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		fetched, err := integrationClient.GetTask(ctx(), task.ID)
		if err != nil {
			t.Fatalf("GetTask() error = %v", err)
		}

		logMetadata(t, "kanban_only", fetched)
		if fetched.Status == nil || *fetched.Status != lunatask.StatusWaiting {
			t.Errorf("Status = %v, want %v", fetched.Status, lunatask.StatusWaiting)
		}
	})

	t.Run("priority_and_motivation", func(t *testing.T) {
		name := testName("metadata-pri-mot")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			Priority(lunatask.PriorityHighest).
			WithMotivation(lunatask.MotivationWant).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		fetched, err := integrationClient.GetTask(ctx(), task.ID)
		if err != nil {
			t.Fatalf("GetTask() error = %v", err)
		}

		logMetadata(t, "priority_motivation", fetched)
		if fetched.Priority == nil || *fetched.Priority != lunatask.PriorityHighest {
			t.Errorf("Priority = %v, want %v", fetched.Priority, lunatask.PriorityHighest)
		}
		if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationWant {
			t.Errorf("Motivation = %v, want %v", fetched.Motivation, lunatask.MotivationWant)
		}
	})

	t.Run("all_eisenhower_quadrants", func(t *testing.T) {
		quadrants := []struct {
			name      string
			quadrant  lunatask.Eisenhower
			important bool
			urgent    bool
		}{
			{"do_now", lunatask.EisenhowerDoNow, true, true},
			{"delegate", lunatask.EisenhowerDelegate, false, true},
			{"do_later", lunatask.EisenhowerDoLater, true, false},
			{"eliminate", lunatask.EisenhowerEliminate, false, false},
		}

		for _, q := range quadrants {
			t.Run(q.name, func(t *testing.T) {
				name := testName("eisenhower-" + q.name)
				task, err := integrationClient.NewTask(name).
					InArea(testAreaID).
					WithEisenhower(q.quadrant).
					Create(ctx())
				if err != nil {
					t.Fatalf("Create() error = %v", err)
				}
				if task == nil {
					t.Fatal("Create() returned nil (duplicate?)")
				}

				t.Cleanup(func() {
					if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
						t.Errorf("DeleteTask() cleanup error = %v", err)
					}
				})

				fetched, err := integrationClient.GetTask(ctx(), task.ID)
				if err != nil {
					t.Fatalf("GetTask() error = %v", err)
				}

				t.Logf("Quadrant %s: eisenhower=%v", q.name, fetched.Eisenhower)
				if fetched.Eisenhower == nil || *fetched.Eisenhower != q.quadrant {
					t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, q.quadrant)
				}
			})
		}
	})

	t.Run("all_status_values", func(t *testing.T) {
		statuses := []lunatask.TaskStatus{
			lunatask.StatusLater,
			lunatask.StatusNext,
			lunatask.StatusInProgress,
			lunatask.StatusWaiting,
		}

		for _, status := range statuses {
			t.Run(string(status), func(t *testing.T) {
				name := testName("status-" + string(status))
				task, err := integrationClient.NewTask(name).
					InArea(testAreaID).
					WithStatus(status).
					Create(ctx())
				if err != nil {
					t.Fatalf("Create() error = %v", err)
				}
				if task == nil {
					t.Fatal("Create() returned nil (duplicate?)")
				}

				t.Cleanup(func() {
					if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
						t.Errorf("DeleteTask() cleanup error = %v", err)
					}
				})

				fetched, err := integrationClient.GetTask(ctx(), task.ID)
				if err != nil {
					t.Fatalf("GetTask() error = %v", err)
				}

				t.Logf("Status %s: status=%v", status, fetched.Status)
				if fetched.Status == nil || *fetched.Status != status {
					t.Errorf("Status = %v, want %v", fetched.Status, status)
				}
			})
		}
	})

	t.Run("update_clears_eisenhower", func(t *testing.T) {
		name := testName("clear-eisenhower")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithEisenhower(lunatask.EisenhowerDoNow).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		updated, err := integrationClient.NewTaskUpdate(task.ID).
			WithEisenhower(lunatask.EisenhowerUncategorized).
			Update(ctx())
		if err != nil {
			t.Fatalf("Update() error = %v", err)
		}

		t.Logf("After clear: eisenhower=%v", updated.Eisenhower)
		if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerUncategorized {
			t.Errorf("Eisenhower = %v, want %v (uncategorized)", updated.Eisenhower, lunatask.EisenhowerUncategorized)
		}
	})

	t.Run("update_preserves_unmodified_fields", func(t *testing.T) {
		name := testName("preserve-fields")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithStatus(lunatask.StatusNext).
			WithEisenhower(lunatask.EisenhowerDoLater).
			Priority(lunatask.PriorityHigh).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		updated, err := integrationClient.NewTaskUpdate(task.ID).
			WithMotivation(lunatask.MotivationShould).
			Update(ctx())
		if err != nil {
			t.Fatalf("Update() error = %v", err)
		}

		logMetadata(t, "after_update", updated)

		if updated.Status == nil || *updated.Status != lunatask.StatusNext {
			t.Errorf("Status changed: %v, want %v", updated.Status, lunatask.StatusNext)
		}
		if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerDoLater {
			t.Errorf("Eisenhower changed: %v, want %v", updated.Eisenhower, lunatask.EisenhowerDoLater)
		}
		if updated.Priority == nil || *updated.Priority != lunatask.PriorityHigh {
			t.Errorf("Priority changed: %v, want %v", updated.Priority, lunatask.PriorityHigh)
		}
		if updated.Motivation == nil || *updated.Motivation != lunatask.MotivationShould {
			t.Errorf("Motivation = %v, want %v", updated.Motivation, lunatask.MotivationShould)
		}
	})

	t.Run("cross_workflow_update", func(t *testing.T) {
		name := testName("cross-workflow")
		task, err := integrationClient.NewTask(name).
			InArea(testAreaID).
			WithEisenhower(lunatask.EisenhowerDoNow).
			Create(ctx())
		if err != nil {
			t.Fatalf("Create() error = %v", err)
		}
		if task == nil {
			t.Fatal("Create() returned nil (duplicate?)")
		}

		t.Cleanup(func() {
			if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil {
				t.Errorf("DeleteTask() cleanup error = %v", err)
			}
		})

		logMetadata(t, "after_create", task)

		updated, err := integrationClient.NewTaskUpdate(task.ID).
			WithStatus(lunatask.StatusWaiting).
			WithMotivation(lunatask.MotivationMust).
			Update(ctx())
		if err != nil {
			t.Fatalf("Update() error = %v", err)
		}

		logMetadata(t, "after_update", updated)

		if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerDoNow {
			t.Errorf("Eisenhower lost after update: %v, want %v", updated.Eisenhower, lunatask.EisenhowerDoNow)
		}
		if updated.Status == nil || *updated.Status != lunatask.StatusWaiting {
			t.Errorf("Status = %v, want %v", updated.Status, lunatask.StatusWaiting)
		}
		if updated.Motivation == nil || *updated.Motivation != lunatask.MotivationMust {
			t.Errorf("Motivation = %v, want %v", updated.Motivation, lunatask.MotivationMust)
		}

		fetched, err := integrationClient.GetTask(ctx(), task.ID)
		if err != nil {
			t.Fatalf("GetTask() error = %v", err)
		}

		logMetadata(t, "after_fetch", fetched)

		if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoNow {
			t.Errorf("Eisenhower lost after fetch: %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoNow)
		}
		if fetched.Status == nil || *fetched.Status != lunatask.StatusWaiting {
			t.Errorf("Status after fetch = %v, want %v", fetched.Status, lunatask.StatusWaiting)
		}
		if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationMust {
			t.Errorf("Motivation after fetch = %v, want %v", fetched.Motivation, lunatask.MotivationMust)
		}
	})
}

func logMetadata(t *testing.T, label string, task *lunatask.Task) {
	t.Helper()
	t.Logf("%s: status=%v eisenhower=%v priority=%v motivation=%v",
		label, task.Status, task.Eisenhower, task.Priority, task.Motivation)
}
