package options

import (
	"strings"
	"testing"

	"github.com/spf13/pflag"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/aws-shared-provider/pkg/utils/tikv"
)

func TestValidateInfraClientOptions(t *testing.T) {
	cases := []struct {
		name        string
		options     Options
		expectedErr string
	}{
		{
			name: "legacy primary",
			options: Options{
				InfraRedisAddr:    "redis:6379",
				InfraClusterTable: "clusters",
			},
		},
		{
			name: "legacy primary with double write",
			options: Options{
				EnableDoubleWrite: true,
				InfraRedisAddr:    "redis:6379",
				InfraClusterTable: "clusters",
				InfraRdsSecretArn: "rds-secret",
			},
		},
		{
			name: "mysql primary does not require legacy configuration",
			options: Options{
				EnableInfraMysqlClient: true,
				InfraRdsSecretArn:      "rds-secret",
			},
		},
		{
			name: "mysql primary double write does not require redis",
			options: Options{
				EnableInfraMysqlClient: true,
				EnableDoubleWrite:      true,
				InfraClusterTable:      "clusters",
				InfraRdsSecretArn:      "rds-secret",
			},
		},
		{
			name:        "legacy cache requires redis",
			options:     Options{InfraClusterTable: "clusters"},
			expectedErr: "infra-redis-addr must set",
		},
		{
			name: "legacy client requires table",
			options: Options{
				InfraRedisAddr: "redis:6379",
			},
			expectedErr: "infra-cluster-table must set",
		},
		{
			name: "mysql client requires rds secret",
			options: Options{
				EnableInfraMysqlClient: true,
			},
			expectedErr: "infra-rds-secret-arn must be set",
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			err := c.options.validateInfraClientOptions()
			if c.expectedErr == "" {
				if err != nil {
					t.Fatalf("validateInfraClientOptions() error = %v", err)
				}
				return
			}
			if err == nil || !strings.Contains(err.Error(), c.expectedErr) {
				t.Fatalf("validateInfraClientOptions() error = %v, want containing %q", err, c.expectedErr)
			}
		})
	}
}

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

	opt := New()
	require.False(t, opt.SkipClusterDeleteEtcdAndPd)

	fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
	opt.AddFlags(fs)
	require.NoError(t, fs.Parse([]string{"--skip-cluster-delete-etcd-and-pd"}))
	require.True(t, opt.SkipClusterDeleteEtcdAndPd)
}

func TestImportMaxConcurrentFromEnv(t *testing.T) {
	t.Run("default", func(t *testing.T) {
		t.Setenv(importMaxConcurrentEnv, "")

		concurrent, err := importMaxConcurrentFromEnv()
		require.NoError(t, err)
		require.Equal(t, 10, concurrent)
	})

	t.Run("configured", func(t *testing.T) {
		t.Setenv(importMaxConcurrentEnv, "25")

		concurrent, err := importMaxConcurrentFromEnv()
		require.NoError(t, err)
		require.Equal(t, 25, concurrent)
	})

	t.Run("invalid", func(t *testing.T) {
		t.Setenv(importMaxConcurrentEnv, "invalid")

		_, err := importMaxConcurrentFromEnv()
		require.Error(t, err)
	})

	t.Run("non-positive", func(t *testing.T) {
		t.Setenv(importMaxConcurrentEnv, "0")

		_, err := importMaxConcurrentFromEnv()
		require.EqualError(t, err, "IMPORT_MAX_CONCURRENT must be greater than 0")
	})
}

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

	opt := New()
	require.Equal(t, tikv.RestoreModePitr, opt.BranchRestoreMode)

	fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
	opt.AddFlags(fs)
	require.NoError(t, fs.Parse([]string{"--branch-restore-mode=online_snapshot"}))
	require.Equal(t, tikv.RestoreModeOnlineSnapshot, opt.BranchRestoreMode)
}

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

	opt := New()
	fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
	opt.AddFlags(fs)
	require.NoError(t, fs.Parse([]string{"--branch-restore-mode=snapshot"}))
	_, err := opt.Config()
	require.ErrorContains(t, err, "invalid --branch-restore-mode")
}
