package connector_inbound_routing

import "context"

// RouteResolver resolves routing decisions for connector inbound events.
//
// There are two resolution modes:
//
//  1. ResolveFlow — called by the Dispatcher at enqueue time.  The caller
//     resolves the active pack incarnation once (via
//     connector_events.Service.ResolveActivePack) and passes packInstallID so
//     the route lookup is scoped to the same incarnation used for enqueue.
//     This eliminates the install-transition race: without the packInstallID
//     binding, an apply/rebirth between route-resolution and enqueue could
//     produce impossible (InstalledPackId=B, RouteVersion=A) tuples that the
//     Worker's exact-version lookup would miss or misroute.
//     Returns the latest (version, flowDeveloperName) for the
//     (packInstallID, eventKind) pair.  The Dispatcher snapshots the returned
//     Version on the queue row; the Worker later calls ResolveFlowByVersion to
//     obtain the exact, immutable flow name for that version.
//
//  2. ResolveFlowByVersion — called by the Worker at processing time.  Returns
//     the FlowDeveloperName for the exact (installedPackID, eventKind, version)
//     triple.  Because route rows are immutable per version, the result is the
//     same regardless of when the Worker processes the event, even after the
//     pack has re-applied with a different FlowDeveloperName.
//
// Production wiring: wire NewDBRouteResolver(db) in cmd/server or the
// application service layer. Tests wire NoopRouteResolver or a mock.
type RouteResolver interface {
	// ResolveFlow returns the latest version and flow name for
	// (packInstallID, eventKind).  The packInstallID argument scopes the lookup
	// to the event's own pack incarnation — this prevents
	// (InstalledPackId=B, RouteVersion=A) snapshot inconsistencies during
	// install transitions.  Routes are append-only per (InstalledPackId,
	// EventKind, Version); resolver returns the latest live Version for the
	// specified incarnation.
	// Returns ErrRouteNotDeclared if no live route exists for this triple.
	ResolveFlow(ctx context.Context, namespace, packInstallID, eventKind string) (version int64, flowDeveloperName string, err error)

	// ResolveFlowByVersion returns the flow name for the exact
	// (installedPackID, eventKind, version) triple.  Used by the Worker at
	// processing time; the Version was snapshotted on the queue row at enqueue.
	// Returns ErrRouteNotDeclared if no row with that exact Version exists
	// (e.g. the version row was swept after all referencing queue rows were
	// processed — ErrRouteVersionSwept is a more specific sentinel for that case).
	ResolveFlowByVersion(ctx context.Context, installedPackID, eventKind string, version int64) (flowDeveloperName string, err error)

	// ResolveExecutionSpecByVersion returns the full immutable execution
	// contract for the exact (installedPackID, eventKind, version) triple.
	ResolveExecutionSpecByVersion(ctx context.Context, installedPackID, eventKind string, version int64) (RouteExecutionSpec, error)
}

// NoopRouteResolver always returns ErrRouteNotDeclared. Use in tests or as a
// compile-time check that a component implements RouteResolver.
type NoopRouteResolver struct{}

func (NoopRouteResolver) ResolveFlow(_ context.Context, _, _, _ string) (int64, string, error) {
	return 0, "", ErrRouteNotDeclared
}

func (NoopRouteResolver) ResolveFlowByVersion(_ context.Context, _, _ string, _ int64) (string, error) {
	return "", ErrRouteNotDeclared
}

func (NoopRouteResolver) ResolveExecutionSpecByVersion(_ context.Context, _, _ string, _ int64) (RouteExecutionSpec, error) {
	return RouteExecutionSpec{}, ErrRouteNotDeclared
}
