package clusterupgradeoperation

import (
	"context"
	"errors"
	"testing"

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

	o11yv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/clusterupgradeoperation/providers"
)

func TestMockCloudProvider_ListClusterAddons(t *testing.T) {
	mock := &MockCloudProvider{}
	addons, err := mock.ListClusterAddons(context.Background(), "cluster-1")
	assert.NoError(t, err)
	assert.Nil(t, addons)
}

func TestMockCloudProvider_GetNodeGroupsByLabels(t *testing.T) {
	t.Run("success_with_matching_labels", func(t *testing.T) {
		mock := &MockCloudProvider{
			NodeGroups: []providers.NodeGroup{
				{ID: "ng-1", Name: "ng-1", Labels: map[string]string{"env": "prod"}},
				{ID: "ng-2", Name: "ng-2", Labels: map[string]string{"env": "prod", "team": "a"}},
				{ID: "ng-3", Name: "ng-3", Labels: map[string]string{"env": "dev"}},
			},
		}
		// Subset match (same as GKE/EKS/AKS/ACK): selector keys must match; nodeGroup may have extra labels
		ngs, err := mock.GetNodeGroupsByLabels(context.Background(), "cluster-1", map[string]string{"env": "prod"})
		assert.NoError(t, err)
		require.Len(t, ngs, 2) // ng-1 and ng-2 both have env=prod
		names := []string{ngs[0].Name, ngs[1].Name}
		assert.Contains(t, names, "ng-1")
		assert.Contains(t, names, "ng-2")

		ngs2, err := mock.GetNodeGroupsByLabels(context.Background(), "cluster-1", map[string]string{"env": "prod", "team": "a"})
		assert.NoError(t, err)
		require.Len(t, ngs2, 1)
		assert.Equal(t, "ng-2", ngs2[0].Name)
	})

	t.Run("returns_error_when_GetNodeGroupsErr_set", func(t *testing.T) {
		mock := &MockCloudProvider{
			NodeGroups:       []providers.NodeGroup{{ID: "ng-1", Name: "ng-1"}},
			GetNodeGroupsErr: errors.New("api error"),
		}
		ngs, err := mock.GetNodeGroupsByLabels(context.Background(), "cluster-1", map[string]string{})
		assert.Error(t, err)
		assert.Nil(t, ngs)
	})
}

func TestMockCloudProvider_DescribeCluster(t *testing.T) {
	mock := &MockCloudProvider{}
	result, err := mock.DescribeCluster(context.Background(), "cluster-1")
	assert.NoError(t, err)
	assert.Nil(t, result)
}

func TestMockCloudProviderRegistry_Get(t *testing.T) {
	mockProvider := &MockCloudProvider{}
	registry := &MockCloudProviderRegistry{Provider: mockProvider}

	p, err := registry.Get(o11yv1.CloudProviderEKS)
	assert.NoError(t, err)
	assert.Equal(t, mockProvider, p)
}

func TestMockCloudProviderRegistry_Get_ReturnsErr(t *testing.T) {
	registry := &MockCloudProviderRegistry{
		Provider: &MockCloudProvider{},
		GetErr:   errors.New("registry error"),
	}
	_, err := registry.Get(o11yv1.CloudProviderEKS)
	assert.Error(t, err)
	assert.Equal(t, "registry error", err.Error())
}

func TestMockCloudProviderRegistry_Register(t *testing.T) {
	registry := &MockCloudProviderRegistry{}
	mock := &MockCloudProvider{}

	registry.Register(o11yv1.CloudProviderEKS, mock)

	p, err := registry.Get(o11yv1.CloudProviderEKS)
	assert.NoError(t, err)
	assert.Equal(t, mock, p)
}

func TestMockCloudProvider_DeleteNodeGroup(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		mock := &MockCloudProvider{}
		err := mock.DeleteNodeGroup(context.Background(), "cluster-1", "ng-1")
		assert.NoError(t, err)
	})

	t.Run("returns_error_when_DeleteNodeGroupErr_set", func(t *testing.T) {
		mock := &MockCloudProvider{
			DeleteNodeGroupErr: errors.New("delete failed"),
		}
		err := mock.DeleteNodeGroup(context.Background(), "cluster-1", "ng-1")
		assert.Error(t, err)
		assert.Equal(t, "delete failed", err.Error())
	})
}

func TestMockCloudProvider_GetControlPlaneVersion(t *testing.T) {
	t.Run("returns_version_when_set", func(t *testing.T) {
		mock := &MockCloudProvider{ControlPlaneVersion: "1.28.0"}
		ver, err := mock.GetControlPlaneVersion(context.Background(), "cluster-1")
		assert.NoError(t, err)
		assert.Equal(t, "1.28.0", ver)
	})

	t.Run("returns_error_when_GetControlPlaneVersionErr_set", func(t *testing.T) {
		mock := &MockCloudProvider{
			GetControlPlaneVersionErr: errors.New("api error"),
		}
		ver, err := mock.GetControlPlaneVersion(context.Background(), "cluster-1")
		assert.Error(t, err)
		assert.Empty(t, ver)
	})
}
