package grpcapi

import (
	"context"
	"testing"

	"galaxy/gateway/internal/session"

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

func TestExecuteCommandRejectsInvalidSignature(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service:      delegate,
		SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)

	req := newValidExecuteCommandRequest()
	req.Signature[0] ^= 0xff

	_, err := client.ExecuteCommand(context.Background(), connect.NewRequest(req))
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnauthenticated, connect.CodeOf(err))
	assert.Equal(t, "invalid request signature", connectErrorMessage(t, err))
	assert.Zero(t, delegate.executeCalls)
}

func TestExecuteCommandRejectsWrongKey(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service: delegate,
		SessionCache: staticSessionCache{
			lookupFunc: func(context.Context, string) (session.Record, error) {
				record := newActiveSessionRecord()
				record.ClientPublicKey = alternateTestClientPublicKeyBase64()
				return record, nil
			},
		},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)
	_, err := client.ExecuteCommand(context.Background(), connect.NewRequest(newValidExecuteCommandRequest()))
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnauthenticated, connect.CodeOf(err))
	assert.Equal(t, "invalid request signature", connectErrorMessage(t, err))
	assert.Zero(t, delegate.executeCalls)
}

func TestExecuteCommandRejectsInvalidCachedPublicKey(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service: delegate,
		SessionCache: staticSessionCache{
			lookupFunc: func(context.Context, string) (session.Record, error) {
				record := newActiveSessionRecord()
				record.ClientPublicKey = "%%%not-base64%%%"
				return record, nil
			},
		},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)
	_, err := client.ExecuteCommand(context.Background(), connect.NewRequest(newValidExecuteCommandRequest()))
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnavailable, connect.CodeOf(err))
	assert.Equal(t, "session cache is unavailable", connectErrorMessage(t, err))
	assert.Zero(t, delegate.executeCalls)
}

func TestSubscribeEventsRejectsInvalidSignature(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service:      delegate,
		SessionCache: staticSessionCache{lookupFunc: func(context.Context, string) (session.Record, error) { return newActiveSessionRecord(), nil }},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)

	req := newValidSubscribeEventsRequest()
	req.Signature[0] ^= 0xff

	err := subscribeEventsError(t, context.Background(), client, req)
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnauthenticated, connect.CodeOf(err))
	assert.Equal(t, "invalid request signature", connectErrorMessage(t, err))
	assert.Zero(t, delegate.subscribeCalls)
}

func TestSubscribeEventsRejectsWrongKey(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service: delegate,
		SessionCache: staticSessionCache{
			lookupFunc: func(context.Context, string) (session.Record, error) {
				record := newActiveSessionRecord()
				record.ClientPublicKey = alternateTestClientPublicKeyBase64()
				return record, nil
			},
		},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)
	err := subscribeEventsError(t, context.Background(), client, newValidSubscribeEventsRequest())
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnauthenticated, connect.CodeOf(err))
	assert.Equal(t, "invalid request signature", connectErrorMessage(t, err))
	assert.Zero(t, delegate.subscribeCalls)
}

func TestSubscribeEventsRejectsInvalidCachedPublicKey(t *testing.T) {
	t.Parallel()

	delegate := &recordingEdgeGatewayService{}
	server, runGateway := newTestGateway(t, ServerDependencies{
		Service: delegate,
		SessionCache: staticSessionCache{
			lookupFunc: func(context.Context, string) (session.Record, error) {
				record := newActiveSessionRecord()
				record.ClientPublicKey = "%%%not-base64%%%"
				return record, nil
			},
		},
	})
	defer runGateway.stop(t)

	addr := waitForListenAddr(t, server)
	client := newEdgeClient(t, addr)
	err := subscribeEventsError(t, context.Background(), client, newValidSubscribeEventsRequest())
	require.Error(t, err)
	assert.Equal(t, connect.CodeUnavailable, connect.CodeOf(err))
	assert.Equal(t, "session cache is unavailable", connectErrorMessage(t, err))
	assert.Zero(t, delegate.subscribeCalls)
}
