package bus_test

import (
	"log/slog"
	"testing"

	"github.com/enmanuel/agents/shell/bus"
)

func newBus() *bus.Bus {
	return bus.New(slog.Default())
}

func TestSubscribeAndSend(t *testing.T) {
	b := newBus()
	ch := b.Subscribe("agent-a")

	msg := bus.AgentMessage{From: "orch", To: "agent-a", Kind: bus.KindTask, Payload: map[string]string{"k": "v"}}
	if err := b.Send(msg); err != nil {
		t.Fatalf("Send: %v", err)
	}

	got := <-ch
	if got.Kind != bus.KindTask || got.Payload["k"] != "v" {
		t.Fatalf("unexpected message: %+v", got)
	}
}

func TestUnsubscribeClosesChannel(t *testing.T) {
	b := newBus()
	ch := b.Subscribe("agent-b")

	b.Unsubscribe("agent-b")

	// Channel must be closed — reading from a closed channel returns zero value + ok=false.
	_, ok := <-ch
	if ok {
		t.Fatal("expected channel to be closed after Unsubscribe")
	}
}

func TestUnsubscribeRemovesFromBus(t *testing.T) {
	b := newBus()
	b.Subscribe("agent-c")
	b.Unsubscribe("agent-c")

	// Sending after unsubscribe must return an error, not panic.
	err := b.Send(bus.AgentMessage{To: "agent-c", Kind: "ping"})
	if err == nil {
		t.Fatal("expected error when sending to unsubscribed agent")
	}
}

func TestUnsubscribeIdempotent(t *testing.T) {
	b := newBus()
	b.Subscribe("agent-d")
	// Double unsubscribe must not panic.
	b.Unsubscribe("agent-d")
	b.Unsubscribe("agent-d")
}

func TestUnsubscribeNonExistent(t *testing.T) {
	b := newBus()
	// Unsubscribing an ID that was never subscribed must not panic.
	b.Unsubscribe("does-not-exist")
}

func TestSendToUnknownAgent(t *testing.T) {
	b := newBus()
	err := b.Send(bus.AgentMessage{To: "ghost", Kind: "hello"})
	if err == nil {
		t.Fatal("expected error when sending to unknown agent")
	}
}

func TestResubscribeAfterUnsubscribe(t *testing.T) {
	b := newBus()
	b.Subscribe("agent-e")
	b.Unsubscribe("agent-e")

	// Re-subscribe must work and deliver messages.
	ch2 := b.Subscribe("agent-e")
	msg := bus.AgentMessage{To: "agent-e", Kind: "ping"}
	if err := b.Send(msg); err != nil {
		t.Fatalf("Send after re-subscribe: %v", err)
	}
	got := <-ch2
	if got.Kind != "ping" {
		t.Fatalf("unexpected kind: %q", got.Kind)
	}
}
