package inbox

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

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	runInboxCommand(t, "--db", dbPath, "--json", "init")

	sendOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"send",
		"--from", "leader",
		"--to", "worker-a",
		"--subject", "Implement cancellation",
		"--summary", "Initial request",
	)

	var sendResp map[string]any
	mustDecodeJSON(t, sendOut, &sendResp)
	threadID := nestedString(t, sendResp, "data", "thread", "thread_id")

	cancelOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"cancel",
		"--agent", "leader",
		"--thread", threadID,
		"--reason", "Task superseded by a larger refactor",
	)

	var cancelResp map[string]any
	mustDecodeJSON(t, cancelOut, &cancelResp)
	if status := nestedString(t, cancelResp, "data", "thread", "status"); status != "cancelled" {
		t.Fatalf("expected cancelled thread, got %q", status)
	}
	if kind := nestedString(t, cancelResp, "data", "message", "kind"); kind != "control" {
		t.Fatalf("expected control message, got %q", kind)
	}
}

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

	tempDir := t.TempDir()
	dbPath := filepath.Join(tempDir, "coord.db")
	cancelPath := filepath.Join(tempDir, "cancel.md")
	if err := os.WriteFile(cancelPath, []byte("Cancelled by product decision"), 0o644); err != nil {
		t.Fatalf("write cancel artifact: %v", err)
	}

	runInboxCommand(t, "--db", dbPath, "--json", "init")

	sendOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"send",
		"--from", "leader",
		"--to", "worker-a",
		"--subject", "Implement cancellation",
		"--summary", "Initial request",
	)

	var sendResp map[string]any
	mustDecodeJSON(t, sendOut, &sendResp)
	threadID := nestedString(t, sendResp, "data", "thread", "thread_id")

	runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"cancel",
		"--agent", "leader",
		"--thread", threadID,
		"--reason", "Task superseded by a larger refactor",
		"--artifact", cancelPath,
		"--artifact-kind", "brief",
	)

	showOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"show",
		"--thread", threadID,
	)
	var showResp map[string]any
	mustDecodeJSON(t, showOut, &showResp)

	messages, ok := nestedValue(t, showResp, "data", "messages").([]any)
	if !ok || len(messages) == 0 {
		t.Fatalf("expected non-empty message history, got %#v", nestedValue(t, showResp, "data", "messages"))
	}

	lastMessage, ok := messages[len(messages)-1].(map[string]any)
	if !ok {
		t.Fatalf("expected message object, got %#v", messages[len(messages)-1])
	}
	if got := lastMessage["summary"]; got != "Task superseded by a larger refactor" {
		t.Fatalf("expected cancel summary, got %#v", got)
	}
	if got := lastMessage["body"]; got != "Task superseded by a larger refactor" {
		t.Fatalf("expected cancel body, got %#v", got)
	}
	artifacts, ok := lastMessage["artifacts"].([]any)
	if !ok || len(artifacts) != 1 {
		t.Fatalf("expected one cancel artifact, got %#v", lastMessage["artifacts"])
	}
	artifact, ok := artifacts[0].(map[string]any)
	if !ok {
		t.Fatalf("expected artifact object, got %#v", artifacts[0])
	}
	if got := artifact["path"]; got != cancelPath {
		t.Fatalf("expected artifact path %q, got %#v", cancelPath, got)
	}
	if got := artifact["kind"]; got != "brief" {
		t.Fatalf("expected artifact kind brief, got %#v", got)
	}
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	runInboxCommand(t, "--db", dbPath, "--json", "init")

	stdout, _, exitCode := executeInboxCommand(
		"--db", dbPath,
		"--agent", "leader",
		"--json",
		"cancel",
		"--thread", "thr_missing",
	)
	if exitCode != 40 {
		t.Fatalf("expected not-found exit code 40, got %d with %s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "not_found")
}

