// Package downstream defines the verified internal command contract used by the
// gateway after the authenticated edge pipeline succeeds.
package downstream

import (
	"context"
	"errors"
)

var (
	// ErrRouteNotFound reports that Router does not have an exact-match handler
	// for the supplied authenticated message type.
	ErrRouteNotFound = errors.New("downstream route not found")

	// ErrDownstreamUnavailable reports that the resolved downstream dependency is
	// temporarily unavailable.
	ErrDownstreamUnavailable = errors.New("downstream service is unavailable")
)

// AuthenticatedCommand is the minimum verified unary command context the
// gateway may forward to downstream business services.
type AuthenticatedCommand struct {
	// ProtocolVersion is the authenticated transport protocol version accepted
	// by the gateway.
	ProtocolVersion string

	// UserID is the authenticated user identity resolved from SessionCache.
	UserID string

	// DeviceSessionID is the authenticated device session that originated the
	// command.
	DeviceSessionID string

	// MessageType is the stable exact-match downstream routing key.
	MessageType string

	// TimestampMS is the client-supplied request timestamp that already passed
	// freshness verification.
	TimestampMS int64

	// RequestID is the transport correlation and anti-replay identifier.
	RequestID string

	// TraceID is the optional client-supplied correlation identifier.
	TraceID string

	// PayloadBytes carries the verified opaque business payload bytes.
	PayloadBytes []byte
}

// UnaryResult is the minimum downstream unary result the gateway needs in
// order to build a signed authenticated client response.
type UnaryResult struct {
	// ResultCode is the stable opaque downstream result code returned to the
	// client without business reinterpretation by the gateway.
	ResultCode string

	// PayloadBytes carries the opaque downstream response payload bytes.
	PayloadBytes []byte
}

// Client executes a verified authenticated unary command against one concrete
// downstream service or adapter.
type Client interface {
	// ExecuteCommand executes command and returns the downstream unary result.
	ExecuteCommand(ctx context.Context, command AuthenticatedCommand) (UnaryResult, error)
}

// Router resolves the downstream unary client for one exact authenticated
// message_type value.
type Router interface {
	// Route returns the downstream client for messageType. Implementations must
	// wrap ErrRouteNotFound when the route table does not contain messageType.
	Route(messageType string) (Client, error)
}

// StaticRouter resolves exact message_type literals from an immutable route
// map supplied at construction time.
type StaticRouter struct {
	routes map[string]Client
}

// NewStaticRouter constructs a StaticRouter with a defensive copy of routes.
func NewStaticRouter(routes map[string]Client) *StaticRouter {
	clonedRoutes := make(map[string]Client, len(routes))
	for messageType, client := range routes {
		if client == nil {
			continue
		}
		clonedRoutes[messageType] = client
	}

	return &StaticRouter{routes: clonedRoutes}
}

// Route returns the exact-match client for messageType.
func (r *StaticRouter) Route(messageType string) (Client, error) {
	if r == nil {
		return nil, ErrRouteNotFound
	}

	client, ok := r.routes[messageType]
	if !ok || client == nil {
		return nil, ErrRouteNotFound
	}

	return client, nil
}
