package orch

import (
	"path/filepath"
	"strings"
	"testing"
)

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_001",
		"--goal", "Build blog MVP",
		"--summary", "Public blog plus admin CRUD",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_001",
		"--task", "T1",
		"--title", "Implement retry policy",
		"--summary", "Add retry policy to HTTP client",
		"--default-to", "worker-a",
	)

	showOut := runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "show",
		"--run", "run_blog_001",
	)

	var showResp map[string]any
	mustDecodeJSON(t, showOut, &showResp)
	if got := nestedString(t, showResp, "data", "run", "run_id"); got != "run_blog_001" {
		t.Fatalf("expected run id run_blog_001, got %q", got)
	}
	if got := nestedString(t, showResp, "data", "run", "status"); got != "ready" {
		t.Fatalf("expected run status ready, got %q", got)
	}

	taskCounts, ok := nestedValue(t, showResp, "data", "task_counts").(map[string]any)
	if !ok {
		t.Fatalf("expected task_counts object, got %#v", nestedValue(t, showResp, "data", "task_counts"))
	}
	if got, _ := taskCounts["ready"].(float64); got < 1 {
		t.Fatalf("expected ready task count >= 1, got %#v", taskCounts["ready"])
	}

	data, ok := showResp["data"].(map[string]any)
	if !ok {
		t.Fatalf("expected data object, got %#v", showResp["data"])
	}
	if _, exists := data["tasks"]; exists {
		t.Fatalf("did not expect tasks array in run show response, got %#v", data["tasks"])
	}
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"run", "show",
		"--run", "run_blog_missing",
	)
	if exitCode != 40 {
		t.Fatalf("expected not_found exit code 40, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "not_found")
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_003",
		"--goal", "Validate task add input guards",
	)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_003",
		"--task", "T1",
		"--title", "Implement retry policy",
		"--acceptance-json", `{"done":true`,
	)
	if exitCode != 30 {
		t.Fatalf("expected invalid_input exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_input")
	assertErrorMessageContains(t, stdout, "acceptance-json must be valid JSON")
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_004",
		"--goal", "Validate task priority input",
	)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_004",
		"--task", "T1",
		"--title", "Implement retry policy",
		"--priority", "urgent",
	)
	if exitCode != 30 {
		t.Fatalf("expected invalid_input exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_input")
	assertErrorMessageContains(t, stdout, "priority must be one of low, normal, high")
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_005",
		"--goal", "Validate ready ordering",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_005",
		"--task", "T1",
		"--title", "Low priority task",
		"--priority", "low",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_005",
		"--task", "T2",
		"--title", "Normal priority task",
		"--priority", "normal",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_005",
		"--task", "T3",
		"--title", "High priority task",
		"--priority", "high",
	)

	readyOut := runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"ready",
		"--run", "run_blog_005",
		"--limit", "2",
	)

	var readyResp map[string]any
	mustDecodeJSON(t, readyOut, &readyResp)
	readyTasks := nestedArray(t, readyResp, "data", "tasks")
	if len(readyTasks) != 2 {
		t.Fatalf("expected two ready tasks with limit 2, got %#v", readyTasks)
	}

	firstTask, ok := readyTasks[0].(map[string]any)
	if !ok {
		t.Fatalf("expected first ready task object, got %#v", readyTasks[0])
	}
	secondTask, ok := readyTasks[1].(map[string]any)
	if !ok {
		t.Fatalf("expected second ready task object, got %#v", readyTasks[1])
	}
	if got, _ := firstTask["task_id"].(string); got != "T3" {
		t.Fatalf("expected first ready task T3, got %#v", firstTask["task_id"])
	}
	if got, _ := secondTask["task_id"].(string); got != "T2" {
		t.Fatalf("expected second ready task T2, got %#v", secondTask["task_id"])
	}

	for _, item := range readyTasks {
		task, ok := item.(map[string]any)
		if !ok {
			t.Fatalf("expected ready task object, got %#v", item)
		}
		if got, _ := task["task_id"].(string); got == "T1" {
			t.Fatalf("did not expect low-priority task T1 within limited ready results")
		}
	}
}

func assertErrorMessageContains(t *testing.T, raw string, want string) {
	t.Helper()

	var payload map[string]any
	mustDecodeJSON(t, raw, &payload)
	errorValue, ok := payload["error"].(map[string]any)
	if !ok {
		t.Fatalf("expected error object, got %#v", payload["error"])
	}
	message, _ := errorValue["message"].(string)
	if !strings.Contains(message, want) {
		t.Fatalf("expected error message to contain %q, got %q", want, message)
	}
}
