package backendclient_test

import (
	"context"
	"testing"

	"galaxy/gateway/internal/backendclient"
	"galaxy/gateway/internal/downstream"
	lobbymodel "galaxy/model/lobby"
	ordermodel "galaxy/model/order"
	reportmodel "galaxy/model/report"
	usermodel "galaxy/model/user"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Phase 14 follow-up: every authenticated message-type constant
// declared in `pkg/model/<service>` must be wired into the matching
// route table. Without this regression test, adding a new constant
// without registering it surfaces only at runtime as
// `unimplemented: message_type is not routed` — exactly what the
// owner saw when an outdated gateway image missed
// `user.games.order.get`.
func TestRoutesCoverAllAuthenticatedMessageTypes(t *testing.T) {
	t.Parallel()

	cases := map[string]struct {
		expected []string
		actual   map[string]downstream.Client
	}{
		"user": {
			expected: []string{
				usermodel.MessageTypeGetMyAccount,
				usermodel.MessageTypeUpdateMyProfile,
				usermodel.MessageTypeUpdateMySettings,
				usermodel.MessageTypeListMySessions,
				usermodel.MessageTypeRevokeMySession,
				usermodel.MessageTypeRevokeAllMySessions,
			},
			actual: backendclient.UserRoutes(nil),
		},
		"lobby": {
			expected: []string{
				lobbymodel.MessageTypeMyGamesList,
				lobbymodel.MessageTypePublicGamesList,
				lobbymodel.MessageTypeMyApplicationsList,
				lobbymodel.MessageTypeMyInvitesList,
				lobbymodel.MessageTypeOpenEnrollment,
				lobbymodel.MessageTypeGameCreate,
				lobbymodel.MessageTypeApplicationSubmit,
				lobbymodel.MessageTypeInviteRedeem,
				lobbymodel.MessageTypeInviteDecline,
			},
			actual: backendclient.LobbyRoutes(nil),
		},
		"game": {
			expected: []string{
				ordermodel.MessageTypeUserGamesCommand,
				ordermodel.MessageTypeUserGamesOrder,
				ordermodel.MessageTypeUserGamesOrderGet,
				reportmodel.MessageTypeUserGamesReport,
			},
			actual: backendclient.GameRoutes(nil),
		},
	}

	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			t.Parallel()
			require.Len(t, tc.actual, len(tc.expected),
				"%s routes table size diverges from the expected message-type list", name)
			for _, mt := range tc.expected {
				client, ok := tc.actual[mt]
				assert.Truef(t, ok, "%s routes are missing %q", name, mt)
				assert.NotNilf(t, client, "%s routes resolve %q to a nil client", name, mt)
			}
		})
	}
}

// Sanity-check that the order-get route really points at the game
// command client (and not, say, the lobby one if a future refactor
// reshuffles the helpers): the route table must dispatch through
// `gameCommandClient.ExecuteCommand`, which in turn calls
// `RESTClient.ExecuteGameCommand`. We exercise this through the
// public Router contract.
func TestUserGamesOrderGetRoutedToGameClient(t *testing.T) {
	t.Parallel()

	routes := backendclient.GameRoutes(nil)
	router := downstream.NewStaticRouter(routes)

	client, err := router.Route(ordermodel.MessageTypeUserGamesOrderGet)
	require.NoError(t, err)
	require.NotNil(t, client)

	// Without a live RESTClient the client is the unavailable stub —
	// calling ExecuteCommand surfaces the canonical "downstream
	// service is unavailable" sentinel rather than the "not routed"
	// error we want to keep regression-tested.
	_, err = client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
		MessageType: ordermodel.MessageTypeUserGamesOrderGet,
	})
	assert.ErrorIs(t, err, downstream.ErrDownstreamUnavailable)
}
