package resourcemanager

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"

	v1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/errors"
	"github.com/tidbcloud/observability/pkg/common/message"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func Test_integrationTaskReaderWriter_CreateAndDelete(t *testing.T) {
	// disturbance term
	err := integrationTaskRW.CreateIntegrationTask(context.TODO(), &v1.IntegrationTask{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "disturbance",
			Namespace: constants.ObservabilityNamespace,
		},
		Spec: v1.IntegrationTaskSpec{
			TaskType:              v1.SlowQueryTask,
			ClusterID:             "cluster-disturbance",
			SourceBucketName:      "source-bucket",
			DestinationBucketName: "dest-bucket",
		},
	})
	assert.NoError(t, err)
	defer integrationTaskRW.DeleteIntegrationTask(context.TODO(), client.ObjectKey{
		Name:      "disturbance",
		Namespace: constants.ObservabilityNamespace,
	})

	defaultTaskName := "task111"
	defaultTask := &v1.IntegrationTask{
		ObjectMeta: metav1.ObjectMeta{
			Name:      defaultTaskName,
			Namespace: constants.ObservabilityNamespace,
			Labels: map[string]string{
				constants.LabelIntegrationTaskID: "task-id-111",
				constants.LabelApplicationID:     "cluster-111",
				constants.LabelTenantID:          "defaultTenant",
				constants.LabelProjectID:         "defaultProject",
			},
		},
		Spec: v1.IntegrationTaskSpec{
			TaskType:              v1.SlowQueryTask,
			ClusterID:             "cluster-111",
			SourceBucketName:      "source-bucket",
			DestinationBucketName: "dest-bucket",
		},
	}

	exists, err := integrationTaskRW.IsIntegrationTaskExists(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.NoError(t, err)
	assert.False(t, exists)

	_, err = integrationTaskRW.GetIntegrationTask(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.Error(t, err)
	assert.Equal(t, errors.IntegrationTaskNotFound, err.(errors.Error).Code())

	err = integrationTaskRW.CreateIntegrationTask(context.TODO(), defaultTask)
	assert.NoError(t, err)

	exists, err = integrationTaskRW.IsIntegrationTaskExists(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.NoError(t, err)
	assert.True(t, exists)

	defaultTask.Spec.ClusterID = "cluster-updated"
	err = integrationTaskRW.UpdateIntegrationTask(context.TODO(), defaultTask)
	assert.NoError(t, err)

	task, err := integrationTaskRW.GetIntegrationTask(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.NoError(t, err)
	assert.Equal(t, defaultTaskName, task.Name)
	assert.Equal(t, "cluster-updated", task.Spec.ClusterID)

	err = integrationTaskRW.DeleteIntegrationTask(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.NoError(t, err)

	exists, err = integrationTaskRW.IsIntegrationTaskExists(context.TODO(), client.ObjectKey{
		Name:      defaultTaskName,
		Namespace: constants.ObservabilityNamespace,
	})
	assert.NoError(t, err)
	assert.False(t, exists)
}

func Test_integrationTaskReaderWriter_GetIntegrationTaskByID(t *testing.T) {
	clusterId := "cluster-222"
	taskId := "task-id-222"
	taskName := "task222"

	task := &v1.IntegrationTask{
		ObjectMeta: metav1.ObjectMeta{
			Name:      taskName,
			Namespace: constants.ObservabilityNamespace,
			Labels: map[string]string{
				constants.LabelIntegrationTaskID: taskId,
				constants.LabelApplicationID:     clusterId,
			},
		},
		Spec: v1.IntegrationTaskSpec{
			TaskType:              v1.SlowQueryTask,
			ClusterID:             clusterId,
			SourceBucketName:      "source-bucket",
			DestinationBucketName: "dest-bucket",
		},
	}

	err := integrationTaskRW.CreateIntegrationTask(context.TODO(), task)
	assert.NoError(t, err)
	defer integrationTaskRW.DeleteIntegrationTask(context.TODO(), client.ObjectKey{
		Name:      taskName,
		Namespace: constants.ObservabilityNamespace,
	})

	retrievedTask, err := integrationTaskRW.GetIntegrationTaskByID(context.TODO(), clusterId, taskId)
	assert.NoError(t, err)
	assert.Equal(t, taskName, retrievedTask.Name)
	assert.Equal(t, clusterId, retrievedTask.Spec.ClusterID)

	_, err = integrationTaskRW.GetIntegrationTaskByID(context.TODO(), "non-existent", "non-existent")
	assert.Error(t, err)
	assert.Equal(t, errors.IntegrationTaskNotFound, err.(errors.Error).Code())
}

func Test_integrationTaskReaderWriter_QueryAndListIntegrationTasks(t *testing.T) {
	// Create tasks for testing
	tasks := []*v1.IntegrationTask{
		{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "task1",
				Namespace: constants.ObservabilityNamespace,
				Labels: map[string]string{
					"env":                            "test",
					constants.LabelIntegrationTaskID: "task-id-1",
					constants.LabelApplicationID:     "cluster-1",
				},
			},
			Spec: v1.IntegrationTaskSpec{
				TaskType:              v1.SlowQueryTask,
				ClusterID:             "cluster-1",
				SourceBucketName:      "source-bucket-1",
				DestinationBucketName: "dest-bucket-1",
			},
		},
		{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "task2",
				Namespace: constants.ObservabilityNamespace,
				Labels: map[string]string{
					"env":                            "test",
					constants.LabelIntegrationTaskID: "task-id-2",
					constants.LabelApplicationID:     "cluster-2",
				},
			},
			Spec: v1.IntegrationTaskSpec{
				TaskType:              v1.SlowQueryTask,
				ClusterID:             "cluster-2",
				SourceBucketName:      "source-bucket-2",
				DestinationBucketName: "dest-bucket-2",
			},
		},
	}

	for _, task := range tasks {
		err := integrationTaskRW.CreateIntegrationTask(context.TODO(), task)
		assert.NoError(t, err)
	}

	// Verify tasks exist
	for _, task := range tasks {
		exists, err := integrationTaskRW.IsIntegrationTaskExists(context.TODO(), client.ObjectKey{
			Name:      task.Name,
			Namespace: constants.ObservabilityNamespace,
		})
		assert.NoError(t, err)
		assert.True(t, exists)
	}

	// Test QueryIntegrationTasks with filters
	taskList, err := integrationTaskRW.QueryIntegrationTasks(
		context.TODO(),
		map[string]string{"env": "test"},
		message.ContinueRequest{},
	)
	assert.NoError(t, err)
	assert.GreaterOrEqual(t, len(taskList.Items), 2)

	// Clean up
	for _, task := range tasks {
		err := integrationTaskRW.DeleteIntegrationTask(context.TODO(), client.ObjectKey{
			Name:      task.Name,
			Namespace: constants.ObservabilityNamespace,
		})
		assert.NoError(t, err)
	}

	// Verify tasks are deleted
	for _, task := range tasks {
		exists, err := integrationTaskRW.IsIntegrationTaskExists(context.TODO(), client.ObjectKey{
			Name:      task.Name,
			Namespace: constants.ObservabilityNamespace,
		})
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func Test_integrationTaskReaderWriter_QueryWithPagination(t *testing.T) {
	// Create multiple tasks for pagination testing
	for i := 0; i < 5; i++ {
		task := &v1.IntegrationTask{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "pagination-task-" + string(rune('a'+i)),
				Namespace: constants.ObservabilityNamespace,
				Labels: map[string]string{
					"pagination": "test",
				},
			},
			Spec: v1.IntegrationTaskSpec{
				TaskType:              v1.SlowQueryTask,
				ClusterID:             "cluster-pagination",
				SourceBucketName:      "source-bucket-pagination",
				DestinationBucketName: "dest-bucket-pagination",
			},
		}
		err := integrationTaskRW.CreateIntegrationTask(context.TODO(), task)
		assert.NoError(t, err)
		defer integrationTaskRW.DeleteIntegrationTask(context.TODO(), client.ObjectKey{
			Name:      task.Name,
			Namespace: constants.ObservabilityNamespace,
		})
	}

	// Test with limit - note: fake client may not respect limit, so we just verify it returns results
	taskList, err := integrationTaskRW.QueryIntegrationTasks(
		context.TODO(),
		map[string]string{"pagination": "test"},
		message.ContinueRequest{Limit: 2},
	)
	assert.NoError(t, err)
	assert.GreaterOrEqual(t, len(taskList.Items), 2)
}
