package nodegroup

import (
	"context"
	"errors"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/stretchr/testify/assert"
	"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"
	dbmocks "github.com/tidbcloud/cluster-service-ng/pkg/externalmocks/dbmocks"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func newNodeInstanceGroupMockDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {
	t.Helper()
	sqlDB, mockDB, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
	require.NoError(t, err)

	gdb, err := gorm.Open(mysql.New(mysql.Config{Conn: sqlDB, SkipInitializeWithVersion: true}), &gorm.Config{SkipDefaultTransaction: true})
	require.NoError(t, err)

	t.Cleanup(func() {
		mockDB.ExpectClose()
		require.NoError(t, sqlDB.Close())
	})

	return gdb, mockDB
}

func TestNodeInstanceGroupRepo_Save(t *testing.T) {
	ctx := context.Background()
	provider := dbmocks.NewProvider(t)
	gdb, mockDB := newNodeInstanceGroupMockDB(t)
	provider.EXPECT().WriteDB(mock.Anything).Return(gdb)
	mockDB.ExpectQuery("SELECT .*node_instance_groups.*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
	mockDB.ExpectExec("UPDATE .*node_instance_groups.*").WillReturnResult(sqlmock.NewResult(1, 1))

	repo := NewNodeInstanceGroupRepo(provider)
	group := &entity.NodeInstanceGroup{
		Name:                "8C16G",
		NodeSpec:            &apiv1.NodeSpec{Cpu: 8, MemoryGi: 16},
		PrimaryInstanceType: "m5.2xlarge",
	}
	require.NoError(t, repo.Save(ctx, group))

	require.NoError(t, mockDB.ExpectationsWereMet())
}

func TestNodeInstanceGroupRepo_Load_NotFound(t *testing.T) {
	ctx := context.Background()
	provider := dbmocks.NewProvider(t)
	gdb, mockDB := newNodeInstanceGroupMockDB(t)
	provider.EXPECT().ReadDB(mock.Anything).Return(gdb)
	mockDB.ExpectQuery("SELECT .*node_instance_groups.*").WithArgs("missing", sqlmock.AnyArg()).WillReturnRows(sqlmock.NewRows([]string{"id"}))

	repo := NewNodeInstanceGroupRepo(provider)
	group, err := repo.Load(ctx, "missing")
	require.Error(t, err)
	assert.Equal(t, codes.NotFound, status.Code(err))
	assert.Nil(t, group)

	require.NoError(t, mockDB.ExpectationsWereMet())
}

func TestNodeInstanceGroupRepo_Load_Error(t *testing.T) {
	ctx := context.Background()
	provider := dbmocks.NewProvider(t)
	gdb, mockDB := newNodeInstanceGroupMockDB(t)
	provider.EXPECT().ReadDB(mock.Anything).Return(gdb)
	mockDB.ExpectQuery("SELECT .*node_instance_groups.*").WithArgs("boom", sqlmock.AnyArg()).WillReturnError(errors.New("boom"))

	repo := NewNodeInstanceGroupRepo(provider)
	group, err := repo.Load(ctx, "boom")
	require.Error(t, err)
	assert.Equal(t, codes.Internal, status.Code(err))
	assert.Nil(t, group)

	require.NoError(t, mockDB.ExpectationsWereMet())
}
