package orch

import (
	"path/filepath"
	"testing"
)

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	threadID := seedBlockedTaskForAnswerCleanupEdgeTests(t, dbPath, "run_blog_answer_001", "T2", "worker-b")

	answerOut := runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"answer",
		"--run", "run_blog_answer_001",
		"--task", "T2",
		"--payload-json", `{"decision":"stdout","source":"leader"}`,
	)

	var answerResp map[string]any
	mustDecodeJSON(t, answerOut, &answerResp)
	message, ok := nestedValue(t, answerResp, "data", "message").(map[string]any)
	if !ok {
		t.Fatalf("expected answer message object, got %#v", nestedValue(t, answerResp, "data", "message"))
	}
	if got, _ := message["kind"].(string); got != "answer" {
		t.Fatalf("expected answer message kind, got %#v", message["kind"])
	}
	payload, ok := message["payload_json"].(map[string]any)
	if !ok {
		t.Fatalf("expected payload_json object, got %#v", message["payload_json"])
	}
	if got, _ := payload["decision"].(string); got != "stdout" {
		t.Fatalf("expected payload decision stdout, got %#v", payload["decision"])
	}
	if got, _ := payload["source"].(string); got != "leader" {
		t.Fatalf("expected payload source leader, got %#v", payload["source"])
	}

	showOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"show",
		"--thread", threadID,
	)

	var showResp map[string]any
	mustDecodeJSON(t, showOut, &showResp)
	messages := nestedArray(t, showResp, "data", "messages")
	if len(messages) == 0 {
		t.Fatalf("expected messages in thread %s", threadID)
	}
	lastMessage, ok := messages[len(messages)-1].(map[string]any)
	if !ok {
		t.Fatalf("expected last message object, got %#v", messages[len(messages)-1])
	}
	if got, _ := lastMessage["kind"].(string); got != "answer" {
		t.Fatalf("expected latest message kind answer, got %#v", lastMessage["kind"])
	}
	lastPayload, ok := lastMessage["payload_json"].(map[string]any)
	if !ok {
		t.Fatalf("expected latest payload_json object, got %#v", lastMessage["payload_json"])
	}
	if got, _ := lastPayload["decision"].(string); got != "stdout" {
		t.Fatalf("expected latest payload decision stdout, got %#v", lastPayload["decision"])
	}
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	_ = seedBlockedTaskForAnswerCleanupEdgeTests(t, dbPath, "run_blog_answer_002", "T2", "worker-b")

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"answer",
		"--run", "run_blog_answer_002",
		"--task", "T2",
	)
	if exitCode != 30 {
		t.Fatalf("expected exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_input")
}

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

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

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_cleanup_002",
		"--goal", "Validate cleanup selectors",
	)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"cleanup",
		"--run", "run_blog_cleanup_002",
		"--attempt", "1",
	)
	if exitCode != 30 {
		t.Fatalf("expected exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_input")
}

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

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

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", "run_blog_cleanup_003",
		"--goal", "Validate cleanup empty result",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", "run_blog_cleanup_003",
		"--task", "T1",
		"--title", "Prepare cleanup target",
	)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"cleanup",
		"--run", "run_blog_cleanup_003",
		"--task", "T1",
	)
	if exitCode != 10 {
		t.Fatalf("expected exit code 10, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "no_matching_work")
}

func seedBlockedTaskForAnswerCleanupEdgeTests(t *testing.T, dbPath, runID, taskID, agent string) string {
	t.Helper()

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"run", "init",
		"--run", runID,
		"--goal", "Prepare blocked task for answer edge tests",
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"task", "add",
		"--run", runID,
		"--task", taskID,
		"--title", "Build frontend",
		"--default-to", agent,
	)

	dispatchOut := runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"dispatch",
		"--run", runID,
		"--task", taskID,
	)

	var dispatchResp map[string]any
	mustDecodeJSON(t, dispatchOut, &dispatchResp)
	threadID := nestedString(t, dispatchResp, "data", "attempt", "thread_id")

	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"claim",
		"--agent", agent,
		"--thread", threadID,
	)
	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"update",
		"--agent", agent,
		"--thread", threadID,
		"--status", "blocked",
		"--summary", "Need logging decision",
		"--payload-json", `{"question":"stdout or stderr?"}`,
	)
	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"reconcile",
		"--run", runID,
	)

	return threadID
}
