package inbox

import "testing"

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

	dbPath := initCommandTestDB(t)
	sendPendingThread(t, dbPath, "leader", "worker-a", "Implement task", "Create API endpoint")

	fetchOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-a",
		"--status", "pending",
	)

	var fetchResp map[string]any
	mustDecodeJSON(t, fetchOut, &fetchResp)
	threads, ok := nestedValue(t, fetchResp, "data", "threads").([]any)
	if !ok || len(threads) < 1 {
		t.Fatalf("expected at least one fetched thread, got %#v", nestedValue(t, fetchResp, "data", "threads"))
	}
	thread, ok := threads[0].(map[string]any)
	if !ok {
		t.Fatalf("expected thread object, got %#v", threads[0])
	}
	if got, _ := thread["assigned_to"].(string); got != "worker-a" {
		t.Fatalf("expected assigned_to worker-a, got %#v", thread["assigned_to"])
	}
	if got, _ := thread["status"].(string); got != "pending" {
		t.Fatalf("expected pending status, got %#v", thread["status"])
	}
}

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

	dbPath := initCommandTestDB(t)
	sendPendingThread(t, dbPath, "leader", "worker-a", "Task A", "Pending task")
	blockedThreadID := sendPendingThread(t, dbPath, "leader", "worker-a", "Task B", "Blocked task")

	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"claim",
		"--agent", "worker-a",
		"--thread", blockedThreadID,
	)
	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"update",
		"--agent", "worker-a",
		"--thread", blockedThreadID,
		"--status", "blocked",
		"--summary", "Need decision",
		"--payload-json", `{"question":"continue?"}`,
	)

	fetchOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-a",
		"--status", "pending,blocked",
		"--limit", "1",
	)

	var fetchResp map[string]any
	mustDecodeJSON(t, fetchOut, &fetchResp)
	threads, ok := nestedValue(t, fetchResp, "data", "threads").([]any)
	if !ok {
		t.Fatalf("expected threads array, got %#v", nestedValue(t, fetchResp, "data", "threads"))
	}
	if len(threads) > 1 {
		t.Fatalf("expected at most one thread with limit=1, got %d", len(threads))
	}
	if len(threads) == 0 {
		t.Fatalf("expected one thread with status filter, got empty result")
	}
	thread, ok := threads[0].(map[string]any)
	if !ok {
		t.Fatalf("expected thread object, got %#v", threads[0])
	}
	status, _ := thread["status"].(string)
	if status != "pending" && status != "blocked" {
		t.Fatalf("expected pending or blocked status, got %#v", thread["status"])
	}
}

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

	dbPath := initCommandTestDB(t)
	threadID := sendPendingThread(t, dbPath, "leader", "worker-e", "Review navbar copy", "Check top nav wording")

	firstFetchOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-e",
		"--status", "pending",
		"--unread",
	)

	var firstFetchResp map[string]any
	mustDecodeJSON(t, firstFetchOut, &firstFetchResp)
	firstThreads, ok := nestedValue(t, firstFetchResp, "data", "threads").([]any)
	if !ok || len(firstThreads) != 1 {
		t.Fatalf("expected one unread thread before mark-read, got %#v", nestedValue(t, firstFetchResp, "data", "threads"))
	}

	runInboxCommand(
		t,
		"--db", dbPath,
		"--agent", "worker-e",
		"--json",
		"show",
		"--thread", threadID,
		"--mark-read",
	)

	stdout, _, exitCode := executeInboxCommand(
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-e",
		"--status", "pending",
		"--unread",
	)
	if exitCode != 10 {
		t.Fatalf("expected unread fetch to return no_matching_work after mark-read, got exit=%d stdout=%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "no_matching_work")

	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"send",
		"--from", "leader",
		"--to", "worker-e",
		"--thread", threadID,
		"--summary", "Use sentence case",
		"--body", "Keep the nav labels in sentence case.",
	)

	thirdFetchOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-e",
		"--status", "pending",
		"--unread",
	)
	var thirdFetchResp map[string]any
	mustDecodeJSON(t, thirdFetchOut, &thirdFetchResp)
	thirdThreads, ok := nestedValue(t, thirdFetchResp, "data", "threads").([]any)
	if !ok || len(thirdThreads) != 1 {
		t.Fatalf("expected unread thread to reappear after new message, got %#v", nestedValue(t, thirdFetchResp, "data", "threads"))
	}
}

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

	dbPath := initCommandTestDB(t)
	stdout, _, exitCode := executeInboxCommand(
		"--db", dbPath,
		"--json",
		"fetch",
		"--agent", "worker-z",
		"--status", "pending",
	)
	if exitCode != 10 {
		t.Fatalf("expected exit code 10, got %d", exitCode)
	}
	assertErrorJSON(t, stdout, "no_matching_work")
}
