// Copyright 2023 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package importinto

import (
	"context"
	"testing"

	"github.com/google/uuid"
	"github.com/pingcap/tidb/pkg/disttask/framework/proto"
	"github.com/pingcap/tidb/pkg/lightning/backend"
	"github.com/pingcap/tidb/pkg/lightning/common"
	"github.com/pingcap/tidb/pkg/lightning/log"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
)

type cleanupTrackingBackend struct {
	backend.Backend
	cleanedEngineUUIDs []uuid.UUID
}

func (b *cleanupTrackingBackend) CleanupEngine(_ context.Context, engineUUID uuid.UUID) error {
	b.cleanedEngineUUIDs = append(b.cleanedEngineUUIDs, engineUUID)
	return nil
}

func TestImportTaskExecutor(t *testing.T) {
	ctx := context.Background()
	executor := NewImportExecutor(
		ctx,
		":4000",
		&proto.Task{
			TaskBase: proto.TaskBase{ID: 1},
		},
		nil,
		nil,
	).(*importExecutor)

	require.NotNil(t, executor.BaseTaskExecutor.Extension)
	require.True(t, executor.IsIdempotent(&proto.Subtask{}))

	for _, step := range []proto.Step{
		proto.ImportStepImport,
		proto.ImportStepEncodeAndSort,
		proto.ImportStepMergeSort,
		proto.ImportStepWriteAndIngest,
		proto.ImportStepPostProcess,
	} {
		exe, err := executor.GetStepExecutor(&proto.Task{TaskBase: proto.TaskBase{Step: step}, Meta: []byte("{}")})
		require.NoError(t, err)
		require.NotNil(t, exe)
	}
	_, err := executor.GetStepExecutor(&proto.Task{TaskBase: proto.TaskBase{Step: proto.StepInit}, Meta: []byte("{}")})
	require.Error(t, err)
	_, err = executor.GetStepExecutor(&proto.Task{TaskBase: proto.TaskBase{Step: proto.ImportStepImport}, Meta: []byte("")})
	require.Error(t, err)
}

func TestWriteAndIngestStepExecutorCleanupRemoteEngines(t *testing.T) {
	trackingBackend := &cleanupTrackingBackend{}
	dataEngineUUID := uuid.New()
	indexEngineUUID := uuid.New()
	logger := log.Logger{Logger: zap.NewNop()}

	executor := &writeAndIngestStepExecutor{
		logger: zap.NewNop(),
	}
	executor.recordClosedEngine(backend.NewClosedEngine(trackingBackend, logger, dataEngineUUID, 0))
	executor.recordClosedEngine(backend.NewClosedEngine(trackingBackend, logger, indexEngineUUID, common.IndexEngineID))

	executor.cleanupRemoteEngines()

	require.ElementsMatch(t, []uuid.UUID{dataEngineUUID, indexEngineUUID}, trackingBackend.cleanedEngineUUIDs)
}
