package cluster

import (
	"strconv"
	"testing"

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

	clusterrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster"
	clusterrepoimpl "github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/cluster"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
)

// TestClusterRepoGet_DataplaneIdEnrichment verifies that Get correctly
// enriches the DataplaneId from the resource_pools table without the
// GORM session-reuse bug (Unknown column 'dataplane_id' in 'field list').
func TestClusterRepoGet_DataplaneIdEnrichment(t *testing.T) {
	pvd := *dbProvider.Load()
	ctx := t.Context()
	wdb := pvd.WriteDB(ctx)

	// Create a resource pool.
	rp := &schema.ResourcePool{
		ID:         9001,
		Name:       "test-pool",
		OwnerType:  1,
		OwnerId:    1,
		ProviderId: 1,
		RegionId:   1,

		DataplaneId: "dp-abc-123",
	}
	require.NoError(t, wdb.Create(rp).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(rp) })

	// Create a cluster that references the resource pool.
	cluster := &schema.Cluster{
		ID:           90001,
		TenantId:     1001,
		ProviderId:   1,
		RegionId:     1,
		State:        "AVAILABLE",
		SharedPoolId: strconv.FormatUint(rp.ID, 10),
		Attributes:   schema.ClusterAttributes{},
		RuVersion:    1,
	}
	require.NoError(t, wdb.Create(cluster).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(cluster) })

	clusterRepo := clusterrepoimpl.NewClusterRepo(pvd)

	// Get should succeed without "Unknown column 'dataplane_id'" error.
	got, err := clusterRepo.Get(ctx, cluster.ID, false)
	require.Nil(t, err, "Get should not return error; got: %v", err)
	require.NotNil(t, got)
	assert.Equal(t, "dp-abc-123", got.GetDataplaneId())
	assert.Equal(t, cluster.ID, got.GetClusterId())
}

// TestClusterRepoGet_NoSharedPool verifies that Get works for clusters
// without a SharedPoolId (the DataplaneId should be empty).
func TestClusterRepoGet_NoSharedPool(t *testing.T) {
	pvd := *dbProvider.Load()
	ctx := t.Context()
	wdb := pvd.WriteDB(ctx)

	cluster := &schema.Cluster{
		ID:         90002,
		TenantId:   1001,
		ProviderId: 1,
		RegionId:   1,
		State:      "AVAILABLE",
		Attributes: schema.ClusterAttributes{},
		RuVersion:  1,
	}
	require.NoError(t, wdb.Create(cluster).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(cluster) })

	clusterRepo := clusterrepoimpl.NewClusterRepo(pvd)

	got, err := clusterRepo.Get(ctx, cluster.ID, false)
	require.Nil(t, err)
	require.NotNil(t, got)
	assert.Empty(t, got.GetDataplaneId())
}

// TestClusterRepoQuery_DataplaneIdEnrichment verifies that Query correctly
// enriches DataplaneId for multiple clusters without GORM session leaking.
func TestClusterRepoQuery_DataplaneIdEnrichment(t *testing.T) {
	pvd := *dbProvider.Load()
	ctx := t.Context()
	wdb := pvd.WriteDB(ctx)

	rp := &schema.ResourcePool{
		ID:          9002,
		Name:        "test-pool-2",
		OwnerType:   1,
		OwnerId:     1,
		ProviderId:  1,
		RegionId:    1,
		DataplaneId: "dp-xyz-789",
	}
	require.NoError(t, wdb.Create(rp).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(rp) })

	clusterWithPool := &schema.Cluster{
		ID:           90003,
		TenantId:     1001,
		ProviderId:   1,
		RegionId:     1,
		State:        "AVAILABLE",
		SharedPoolId: strconv.FormatUint(rp.ID, 10),
		Attributes:   schema.ClusterAttributes{},
		RuVersion:    1,
	}
	clusterWithoutPool := &schema.Cluster{
		ID:         90004,
		TenantId:   1001,
		ProviderId: 1,
		RegionId:   1,
		State:      "AVAILABLE",
		Attributes: schema.ClusterAttributes{},
		RuVersion:  1,
	}
	require.NoError(t, wdb.Create(clusterWithPool).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(clusterWithPool) })
	require.NoError(t, wdb.Create(clusterWithoutPool).Error)
	t.Cleanup(func() { wdb.Unscoped().Delete(clusterWithoutPool) })

	clusterRepo := clusterrepoimpl.NewClusterRepo(pvd)

	clusters, err := clusterRepo.Query(ctx, &clusterrepo.Filter{
		ClusterIds: []uint64{clusterWithPool.ID, clusterWithoutPool.ID},
	})
	require.Nil(t, err, "Query should not return error; got: %v", err)
	require.Len(t, clusters, 2)

	byID := map[uint64]string{}
	for _, c := range clusters {
		byID[c.GetClusterId()] = c.GetDataplaneId()
	}
	assert.Equal(t, "dp-xyz-789", byID[clusterWithPool.ID])
	assert.Empty(t, byID[clusterWithoutPool.ID])
}

func TestClusterRepoCountLiveBySharedPoolIDs(t *testing.T) {
	pvd := *dbProvider.Load()
	ctx := t.Context()
	wdb := pvd.WriteDB(ctx)

	clusters := []*schema.Cluster{
		{
			ID:           90005,
			TenantId:     1001,
			ProviderId:   1,
			RegionId:     1,
			State:        "AVAILABLE",
			SharedPoolId: "9101",
			Attributes:   schema.ClusterAttributes{},
			RuVersion:    1,
		},
		{
			ID:           90006,
			TenantId:     1001,
			ProviderId:   1,
			RegionId:     1,
			State:        "AVAILABLE",
			SharedPoolId: "9101",
			Attributes:   schema.ClusterAttributes{},
			RuVersion:    1,
		},
		{
			ID:           90007,
			TenantId:     1001,
			ProviderId:   1,
			RegionId:     1,
			State:        "AVAILABLE",
			SharedPoolId: "9102",
			Attributes:   schema.ClusterAttributes{},
			RuVersion:    1,
		},
	}
	for _, cluster := range clusters {
		require.NoError(t, wdb.Create(cluster).Error)
		cluster := cluster
		t.Cleanup(func() { wdb.Unscoped().Delete(cluster) })
	}
	require.NoError(t, wdb.Delete(clusters[1]).Error)

	repo := clusterrepoimpl.NewClusterRepo(pvd)
	counts, err := repo.CountLiveBySharedPoolIDs(ctx, []uint64{9101, 9102, 9103})
	require.Nil(t, err)
	require.Equal(t, map[uint64]int64{9101: 1, 9102: 1}, counts)
}

func TestClusterRepoQueryBySharedPoolIDs(t *testing.T) {
	pvd := *dbProvider.Load()
	ctx := t.Context()
	wdb := pvd.WriteDB(ctx)

	clusters := []*schema.Cluster{
		{
			ID:           90008,
			TenantId:     1001,
			ProviderId:   1,
			RegionId:     1,
			State:        "AVAILABLE",
			SharedPoolId: "9201",
			Attributes:   schema.ClusterAttributes{},
			RuVersion:    1,
		},
		{
			ID:           90009,
			TenantId:     1001,
			ProviderId:   1,
			RegionId:     1,
			State:        "AVAILABLE",
			SharedPoolId: "9202",
			Attributes:   schema.ClusterAttributes{},
			RuVersion:    1,
		},
	}
	for _, cluster := range clusters {
		require.NoError(t, wdb.Create(cluster).Error)
		cluster := cluster
		t.Cleanup(func() { wdb.Unscoped().Delete(cluster) })
	}

	repo := clusterrepoimpl.NewClusterRepo(pvd)
	got, err := repo.Query(ctx, &clusterrepo.Filter{
		OrganizationIds: []uint64{1001},
		SharedPoolIds:   []uint64{9201},
	})

	require.Nil(t, err)
	require.Len(t, got, 1)
	require.Equal(t, uint64(90008), got[0].GetClusterId())
	require.Equal(t, "9201", got[0].GetSharedPoolId())
}
