package server

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

func TestScaleOperationServer_Disabled(t *testing.T) {
	operation := &entity.ScaleOperation{
		ScaleOperation: apiv1.ScaleOperation{
			SharedPoolId:     "100",
			ComponentId:      "tikv-1",
			ScaleOperationId: "op-1",
			Status:           apiv1.ScaleOperationStatus_SCALE_OPERATION_STATUS_RUNNING,
		},
	}
	fakeRepo := &fakeScaleOperationRepo{operation: operation}
	s := &serverImpl{
		scaleOperationRepo: fakeRepo,
		scaleOperationCfg:  &config.ScaleOperationConfig{},
	}

	_, err := s.GetScaleOperation(context.Background(), "100", "op-1")
	require.Error(t, err)
	require.Equal(t, codes.FailedPrecondition, status.Code(err))

	_, err = s.CancelScaleOperation(context.Background(), "100", "op-1", "test")
	require.Error(t, err)
	require.Equal(t, codes.FailedPrecondition, status.Code(err))
}

func TestScaleOperationServer_GetEnabled(t *testing.T) {
	operation := &entity.ScaleOperation{
		ScaleOperation: apiv1.ScaleOperation{
			SharedPoolId:     "100",
			ComponentId:      "tikv-1",
			ScaleOperationId: "op-1",
			Status:           apiv1.ScaleOperationStatus_SCALE_OPERATION_STATUS_RUNNING,
		},
	}
	fakeRepo := &fakeScaleOperationRepo{operation: operation}
	s := &serverImpl{
		scaleOperationRepo: fakeRepo,
		scaleOperationCfg:  &config.ScaleOperationConfig{Enabled: true},
	}

	got, err := s.GetScaleOperation(context.Background(), "100", "op-1")
	require.NoError(t, err)
	require.Equal(t, "op-1", got.GetScaleOperationId())
}

func TestScaleOperationServer_CancelEnabledButUnimplemented(t *testing.T) {
	s := &serverImpl{
		scaleOperationRepo: &fakeScaleOperationRepo{operation: &entity.ScaleOperation{
			ScaleOperation: apiv1.ScaleOperation{
				SharedPoolId:     "100",
				ScaleOperationId: "op-1",
				Status:           apiv1.ScaleOperationStatus_SCALE_OPERATION_STATUS_RUNNING,
			},
		}},
		scaleOperationCfg: &config.ScaleOperationConfig{Enabled: true},
	}

	_, err := s.CancelScaleOperation(context.Background(), "100", "op-1", "test")
	require.Error(t, err)
	require.Equal(t, codes.Unimplemented, status.Code(err))
}

func TestScaleOperationServer_NotFound(t *testing.T) {
	s := &serverImpl{
		scaleOperationRepo: &fakeScaleOperationRepo{},
		scaleOperationCfg:  &config.ScaleOperationConfig{Enabled: true},
	}

	_, err := s.GetScaleOperation(context.Background(), "100", "missing")
	require.Error(t, err)
	require.Equal(t, codes.NotFound, status.Code(err))
}

type fakeScaleOperationRepo struct {
	operation *entity.ScaleOperation
}

func (r *fakeScaleOperationRepo) Create(context.Context, *entity.ScaleOperation) error {
	return nil
}

func (r *fakeScaleOperationRepo) LoadByID(_ context.Context, sharedPoolID, scaleOperationID string, _ bool) (*entity.ScaleOperation, error) {
	if r.operation == nil ||
		r.operation.GetSharedPoolId() != sharedPoolID ||
		r.operation.GetScaleOperationId() != scaleOperationID {
		return nil, gorm.ErrRecordNotFound
	}
	return r.operation, nil
}

func (r *fakeScaleOperationRepo) Save(context.Context, *entity.ScaleOperation) error {
	return nil
}

func (r *fakeScaleOperationRepo) FindActiveByComponent(context.Context, string, string) ([]*entity.ScaleOperation, error) {
	return nil, nil
}

func (r *fakeScaleOperationRepo) ListNonTerminal(context.Context, int) ([]*entity.ScaleOperation, error) {
	return nil, nil
}
