package action

import (
	"errors"
	"testing"

	"github.com/prometheus/client_golang/prometheus/testutil"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/clustergateway"
	cgmocks "github.com/tidbcloud/cluster-service-ng/pkg/clustergateway/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	repomocks "github.com/tidbcloud/cluster-service-ng/pkg/storage/repo/mocks"
)

func TestUpdateResourceGroupAction_RuVersion404(t *testing.T) {
	tests := []struct {
		name              string
		ruVersion         int32
		setRuVersionErr   error
		expectError       bool
		expectMetricDelta float64
	}{
		{
			name:              "ruVersion=2, PD returns 404 → metric incremented, no error",
			ruVersion:         2,
			setRuVersionErr:   clustergateway.ErrRuVersionNotSupported,
			expectError:       false,
			expectMetricDelta: 1,
		},
		{
			name:              "ruVersion=3, PD returns 404 → no metric, no error",
			ruVersion:         3,
			setRuVersionErr:   clustergateway.ErrRuVersionNotSupported,
			expectError:       false,
			expectMetricDelta: 0,
		},
		{
			name:              "ruVersion=2, success → no metric, no error",
			ruVersion:         2,
			setRuVersionErr:   nil,
			expectError:       false,
			expectMetricDelta: 0,
		},
		{
			name:              "ruVersion=2, other error → returns error",
			ruVersion:         2,
			setRuVersionErr:   errors.New("connection refused"),
			expectError:       true,
			expectMetricDelta: 0,
		},
		{
			name:              "ruVersion=0, skips SetKeySpaceRuVersion entirely",
			ruVersion:         0,
			setRuVersionErr:   nil,
			expectError:       false,
			expectMetricDelta: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			clusterGatewayCli := cgmocks.NewClient(t)
			sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
			tidbClusterRepo := repomocks.NewTiDBClusterRepo(t)

			tidbCluster := &apiv1.Cluster{
				Id:        "ks-test-123",
				RuVersion: tt.ruVersion,
				RequestUnit: &apiv1.Cluster_RequestUnit{
					Max: 1000,
				},
				SharedPoolId: "sp-1",
			}
			sharedPool := &entity.SharedPool{
				SharedPool: apiv1.SharedPool{Id: "sp-1"},
			}

			tidbClusterRepo.On("Load", mock.Anything, "ks-test-123", false).
				Return(tidbCluster, nil)
			sharedPoolRepo.On("Load", mock.Anything, "sp-1", false).
				Return(sharedPool, nil)
			clusterGatewayCli.On("SetKeySpaceResourceGroup", mock.Anything, sharedPool, "ks-test-123", mock.Anything).
				Return(nil)

			if tt.ruVersion > 0 {
				clusterGatewayCli.On("SetKeySpaceRuVersion", mock.Anything, sharedPool, "ks-test-123", mock.Anything).
					Return(tt.setRuVersionErr)
			}

			action := NewUpdateResourceGroupAction(sharedPoolRepo, tidbClusterRepo, clusterGatewayCli)

			args := actions.ActionArgs{
				workflow.TiDBClusterIdKey: actions.ActionArgString("ks-test-123"),
			}

			// Reset metric before test
			RuVersionUnsupportedTotal.Reset()

			err := action.Invoke(args)

			if tt.expectError {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}

			metricValue := testutil.ToFloat64(RuVersionUnsupportedTotal.WithLabelValues("sp-1", "ks-test-123"))
			require.InDelta(t, tt.expectMetricDelta, metricValue, 1e-9)
		})
	}
}

func TestUpdateResourceGroupAction_TopologyDriven_DoesNotSkip(t *testing.T) {
	clusterGatewayCli := cgmocks.NewClient(t)
	sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
	tidbClusterRepo := repomocks.NewTiDBClusterRepo(t)

	tidbCluster := &apiv1.Cluster{
		Id:        "ks-topology-123",
		RuVersion: 2,
		RequestUnit: &apiv1.Cluster_RequestUnit{
			Max: 1000,
		},
		SharedPoolId: "sp-topology",
	}
	sharedPool := &entity.SharedPool{SharedPool: apiv1.SharedPool{
		Id:           "sp-topology",
		CapacityMode: apiv1.CapacityMode_TOPOLOGY_DRIVEN,
	}}

	tidbClusterRepo.On("Load", mock.Anything, "ks-topology-123", false).Return(tidbCluster, nil)
	sharedPoolRepo.On("Load", mock.Anything, "sp-topology", false).Return(sharedPool, nil)
	clusterGatewayCli.On("SetKeySpaceResourceGroup", mock.Anything, sharedPool, "ks-topology-123", mock.Anything).Return(nil)
	clusterGatewayCli.On("SetKeySpaceRuVersion", mock.Anything, sharedPool, "ks-topology-123", mock.Anything).Return(nil)

	action := NewUpdateResourceGroupAction(sharedPoolRepo, tidbClusterRepo, clusterGatewayCli)
	args := actions.ActionArgs{workflow.TiDBClusterIdKey: actions.ActionArgString("ks-topology-123")}

	require.NoError(t, action.Invoke(args))
}
