package resourcepool

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool/mock_resourcepool"
	resourcepoolsvc "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/resourcepool"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator/mock_idgenerator"
)

type automaticResumeService struct {
	resourcepoolsvc.Service

	requests []*globalngv1.ResumeResourcePoolRequest
	contexts []context.Context
}

func (s *automaticResumeService) ResumeResourcePool(
	ctx context.Context,
	request *globalngv1.ResumeResourcePoolRequest,
) (*globalngv1.ResumeResourcePoolResponse, bizerr.BizError) {
	s.contexts = append(s.contexts, ctx)
	s.requests = append(s.requests, request)
	if request.GetResourcePoolId() == 11 {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "resume failed")
	}
	return &globalngv1.ResumeResourcePoolResponse{OperationId: request.GetOperationId()}, nil
}

func TestHandleAutomaticResumeResourcePoolsContinuesAfterFailure(t *testing.T) {
	ctrl := gomock.NewController(t)
	repo := resourcepoolrepo.NewMockRepo(ctrl)
	idGenerator := mock_idgenerator.NewMockIDGenerator(ctrl)
	service := &automaticResumeService{}

	repo.EXPECT().ListDueAutomaticResume(gomock.Any()).Return([]uint64{11, 12}, nil)
	idGenerator.EXPECT().MustNew().Return(uint64(101))
	idGenerator.EXPECT().MustNew().Return(uint64(102))

	worker := NewWorker(repo, service, nil, idGenerator)
	require.NoError(t, worker.HandleAutomaticResumeResourcePools(t.Context()))
	require.Equal(t, []*globalngv1.ResumeResourcePoolRequest{
		{ResourcePoolId: 11, OperationId: "101"},
		{ResourcePoolId: 12, OperationId: "102"},
	}, service.requests)

	var requestMeta metadata.RequestMeta
	requestMetaJSON, err := metadata.MarshalRequestMetaFromCtx(service.contexts[0])
	require.NoError(t, err)
	require.NoError(t, json.Unmarshal(requestMetaJSON, &requestMeta))
	require.Equal(t, "11", requestMeta.ResourceID)
	require.True(t, requestMeta.NoProjectRequired)
}

func TestHandleAutomaticResumeResourcePoolsReturnsListError(t *testing.T) {
	ctrl := gomock.NewController(t)
	repo := resourcepoolrepo.NewMockRepo(ctrl)
	idGenerator := mock_idgenerator.NewMockIDGenerator(ctrl)

	repo.EXPECT().ListDueAutomaticResume(gomock.Any()).Return(
		nil,
		bizerr.New(bizerr.ErrCodeUnknown, "list failed"),
	)

	worker := NewWorker(repo, &automaticResumeService{}, nil, idGenerator)
	require.Error(t, worker.HandleAutomaticResumeResourcePools(t.Context()))
}
