// Package transport defines the neutral boundary between an agent's core logic
// and the messaging fabric it runs on. It carries NO Matrix (mautrix) types, so
// the same agent code can be driven by Matrix today and by the unibus message
// bus tomorrow, selected per bot behind a feature flag (branch by abstraction).
//
// The two pieces are:
//
//   - InboundMessage: a transport-neutral description of an incoming message.
//     Both the Matrix listener and the unibus subscriber produce one of these.
//   - Transport: the capability an agent depends on to receive messages and
//     send replies. A Matrix adapter and a unibus adapter both implement it.
package transport

import "context"

// InboundMessage is a transport-neutral incoming message. It is the single type
// an agent's message handler receives, regardless of whether the underlying
// fabric is Matrix or unibus. It deliberately avoids any mautrix type.
type InboundMessage struct {
	// RoomID identifies the conversation on the transport (a Matrix room id or a
	// unibus room id). Replies are addressed back to it.
	RoomID string
	// Subject is the bus subject the message arrived on (unibus). Empty for
	// transports that do not have a subject address space (Matrix).
	Subject string

	SenderID   string // stable id of the sender (Matrix user id / unibus endpoint id)
	SenderName string // human-friendly display name, when the transport knows it

	// MsgID is the unique id of this message on its transport: a Matrix event id
	// or a unibus frame MsgID. Used as the reply/thread anchor.
	MsgID    string
	ThreadID string // root message id of the thread, empty if not threaded
	ReplyTo  string // message id this message replies to, empty if none

	Body    string   // plaintext body / content of the message
	Command string   // parsed command name (e.g. "deploy"), empty if not a command
	Args    []string // parsed command arguments

	PowerLevel  int  // sender power level where the transport models one (Matrix); 0 otherwise
	IsDirectMsg bool // the message is a direct/1:1 message to the bot
	IsMention   bool // the message addresses/mentions the bot
}

// OutboundReply is a transport-neutral outgoing reply.
type OutboundReply struct {
	RoomID   string // conversation to reply into
	Subject  string // bus subject to publish to (unibus); ignored by Matrix
	ReplyTo  string // message id being replied to (renders as a reply)
	ThreadID string // thread root to keep the reply inside, empty for top-level
	Markdown string // reply body, in markdown
}

// Handler processes one inbound message. It is the callback an agent registers
// with a Transport via Run.
type Handler func(ctx context.Context, in InboundMessage)

// Transport is the messaging fabric an agent depends on. Implementations:
//   - a Matrix adapter wrapping the existing mautrix client + listener;
//   - a unibus adapter over github.com/enmanuel/unibus/pkg/client.
//
// An agent core that depends only on Transport (not on *mautrix.Client) can be
// pointed at either fabric without code changes.
type Transport interface {
	// Run delivers each inbound message to handler until ctx is cancelled. It
	// blocks for the lifetime of the subscription and returns ctx.Err() (or a
	// transport error) when it stops.
	Run(ctx context.Context, handler Handler) error
	// Reply sends a reply addressed by the OutboundReply envelope.
	Reply(ctx context.Context, out OutboundReply) error
	// Send posts a standalone markdown message to a conversation (no reply anchor).
	Send(ctx context.Context, roomID, markdown string) error
	// Close releases the underlying connection.
	Close() error
}

// PresenceController is an optional capability for transports that model online
// presence (Matrix). unibus does not, so it simply does not implement this and
// callers type-assert for it.
type PresenceController interface {
	SetPresence(ctx context.Context, online bool) error
}

// TypingController is an optional capability for transports that model typing
// indicators (Matrix). Callers type-assert for it.
type TypingController interface {
	SetTyping(ctx context.Context, roomID string, typing bool) error
}
