package main

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/require"
	"gitlab.com/gitlab-org/gitaly/v14/internal/praefect"
	"gitlab.com/gitlab-org/gitaly/v14/internal/praefect/config"
	"gitlab.com/gitlab-org/gitaly/v14/internal/praefect/datastore"
	"gitlab.com/gitlab-org/gitaly/v14/internal/praefect/service/info"
	"gitlab.com/gitlab-org/gitaly/v14/internal/testhelper"
	"gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testdb"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestSetReplicationFactorSubcommand(t *testing.T) {
	t.Parallel()
	db := testdb.New(t)

	for _, tc := range []struct {
		desc   string
		args   []string
		store  praefect.AssignmentStore
		error  error
		stdout string
	}{
		{
			desc:  "unexpected positional arguments",
			args:  []string{"positonal-arg"},
			error: unexpectedPositionalArgsError{Command: "set-replication-factor"},
		},
		{
			desc:  "missing virtual-storage",
			args:  []string{},
			error: requiredParameterError("virtual-storage"),
		},
		{
			desc:  "missing repository",
			args:  []string{"-virtual-storage=virtual-storage"},
			error: requiredParameterError("repository"),
		},
		{
			desc:  "missing replication-factor",
			args:  []string{"-virtual-storage=virtual-storage", "-repository=relative-path"},
			error: requiredParameterError("replication-factor"),
		},
		{
			desc:  "replication factor too small",
			args:  []string{"-virtual-storage=virtual-storage", "-repository=relative-path", "-replication-factor=0"},
			error: status.Error(codes.InvalidArgument, "set replication factor: attempted to set replication factor 0 but minimum is 1"),
		},
		{
			desc:  "replication factor too big",
			args:  []string{"-virtual-storage=virtual-storage", "-repository=relative-path", "-replication-factor=3"},
			error: status.Error(codes.InvalidArgument, "set replication factor: attempted to set replication factor 3 but virtual storage only contains 2 storages"),
		},
		{
			desc:  "virtual storage not found",
			args:  []string{"-virtual-storage=non-existent", "-repository=relative-path", "-replication-factor=2"},
			error: status.Error(codes.InvalidArgument, `set replication factor: virtual storage "non-existent" not found`),
		},
		{
			desc:  "repository not found",
			args:  []string{"-virtual-storage=virtual-storage", "-repository=non-existent", "-replication-factor=2"},
			error: status.Error(codes.InvalidArgument, `set replication factor: repository "virtual-storage"/"non-existent" not found`),
		},
		{
			desc:  "assignments are disabled",
			args:  []string{"-virtual-storage=virtual-storage", "-repository=relative-path", "-replication-factor=1"},
			store: praefect.NewDisabledAssignmentStore(nil),
			error: status.Error(codes.Internal, `set replication factor: assignments are disabled`),
		},
		{
			desc:   "successfully set",
			args:   []string{"-virtual-storage=virtual-storage", "-repository=relative-path", "-replication-factor=2"},
			stdout: "current assignments: primary, secondary\n",
		},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			ctx := testhelper.Context(t)

			db.TruncateAll(t)

			store := tc.store
			if tc.store == nil {
				store = datastore.NewAssignmentStore(db, map[string][]string{"virtual-storage": {"primary", "secondary"}})
			}

			// create a repository record
			require.NoError(t,
				datastore.NewPostgresRepositoryStore(db, nil).CreateRepository(ctx, 1, "virtual-storage", "relative-path", "relative-path", "primary", nil, nil, false, false),
			)

			ln, clean := listenAndServe(t, []svcRegistrar{registerPraefectInfoServer(
				info.NewServer(config.Config{}, nil, store, nil, nil),
			)})
			defer clean()

			stdout := &bytes.Buffer{}
			cmd := &setReplicationFactorSubcommand{stdout: stdout}
			fs := cmd.FlagSet()
			require.NoError(t, fs.Parse(tc.args))
			err := cmd.Exec(fs, config.Config{
				SocketPath: ln.Addr().String(),
			})
			testhelper.RequireGrpcError(t, tc.error, err)
			require.Equal(t, tc.stdout, stdout.String())
		})
	}
}
