package inbox

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

func TestShowReturnsThreadAndMessageHistory(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 feature X",
		"--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",
		"send",
		"--from", "leader",
		"--to", "worker-a",
		"--thread", threadID,
		"--summary", "Follow-up request",
		"--body", "Please include request logging.",
	)

	showOut := runInboxCommand(t, "--db", dbPath, "--json", "show", "--thread", threadID)
	var showResp map[string]any
	mustDecodeJSON(t, showOut, &showResp)
	if got := nestedString(t, showResp, "data", "thread", "thread_id"); got != threadID {
		t.Fatalf("expected thread %q, got %q", threadID, got)
	}

	messages, ok := nestedValue(t, showResp, "data", "messages").([]any)
	if !ok || len(messages) != 2 {
		t.Fatalf("expected two ordered messages, got %#v", nestedValue(t, showResp, "data", "messages"))
	}
	first, ok := messages[0].(map[string]any)
	if !ok {
		t.Fatalf("expected first message object, got %#v", messages[0])
	}
	second, ok := messages[1].(map[string]any)
	if !ok {
		t.Fatalf("expected second message object, got %#v", messages[1])
	}
	if got := first["summary"]; got != "Initial request" {
		t.Fatalf("expected first summary Initial request, got %#v", got)
	}
	if got := second["summary"]; got != "Follow-up request" {
		t.Fatalf("expected second summary Follow-up request, got %#v", got)
	}
}

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

	tempDir := t.TempDir()
	dbPath := filepath.Join(tempDir, "coord.db")
	artifactPath := filepath.Join(tempDir, "task.md")
	if err := os.WriteFile(artifactPath, []byte("task brief"), 0o644); err != nil {
		t.Fatalf("write artifact file: %v", err)
	}

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

	sendOut := runInboxCommand(
		t,
		"--db", dbPath,
		"--json",
		"send",
		"--from", "leader",
		"--to", "worker-a",
		"--subject", "Artifact task",
		"--summary", "Attach brief",
		"--artifact", artifactPath,
		"--artifact-kind", "brief",
		"--artifact-metadata-json", `{"label":"task-brief"}`,
	)
	var sendResp map[string]any
	mustDecodeJSON(t, sendOut, &sendResp)
	threadID := nestedString(t, sendResp, "data", "thread", "thread_id")

	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 messages with artifacts, got %#v", nestedValue(t, showResp, "data", "messages"))
	}
	first, ok := messages[0].(map[string]any)
	if !ok {
		t.Fatalf("expected message object, got %#v", messages[0])
	}
	artifacts, ok := first["artifacts"].([]any)
	if !ok || len(artifacts) != 1 {
		t.Fatalf("expected one artifact, got %#v", first["artifacts"])
	}
	artifact, ok := artifacts[0].(map[string]any)
	if !ok {
		t.Fatalf("expected artifact object, got %#v", artifacts[0])
	}
	if got := artifact["path"]; got != artifactPath {
		t.Fatalf("expected artifact path %q, got %#v", artifactPath, got)
	}
	if got := artifact["kind"]; got != "brief" {
		t.Fatalf("expected artifact kind brief, got %#v", got)
	}
}

func TestShowMarkReadAdvancesReadCursor(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-e",
		"--subject", "Review nav copy",
		"--summary", "Check wording",
	)
	var sendResp map[string]any
	mustDecodeJSON(t, sendOut, &sendResp)
	threadID := nestedString(t, sendResp, "data", "thread", "thread_id")

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

	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 be empty after mark-read, got exit=%d with %s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "no_matching_work")
}

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

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

	stdout, _, exitCode := executeInboxCommand(
		"--db", dbPath,
		"--json",
		"show",
		"--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")
}

