//                           _       _
// __      _____  __ ___   ___  __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
//  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
//   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
//  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
//  CONTACT: hello@weaviate.io
//

package objects

import (
	"context"
	"testing"

	"github.com/sirupsen/logrus/hooks/test"
	"github.com/stretchr/testify/assert"
	"github.com/weaviate/weaviate/entities/models"
	"github.com/weaviate/weaviate/entities/schema"
	"github.com/weaviate/weaviate/entities/vectorindex/hnsw"
	"github.com/weaviate/weaviate/entities/verbosity"
	"github.com/weaviate/weaviate/usecases/auth/authorization/mocks"
	"github.com/weaviate/weaviate/usecases/config"
)

func Test_BatchDelete_RequestValidation(t *testing.T) {
	var (
		vectorRepo *fakeVectorRepo
		manager    *BatchManager
	)

	schema := schema.Schema{
		Objects: &models.Schema{
			Classes: []*models.Class{
				{
					Class: "Foo",
					Properties: []*models.Property{
						{
							Name:         "name",
							DataType:     schema.DataTypeText.PropString(),
							Tokenization: models.PropertyTokenizationWhitespace,
						},
					},
					VectorIndexConfig: hnsw.UserConfig{},
					Vectorizer:        config.VectorizerModuleNone,
				},
			},
		},
	}

	resetAutoSchema := func(autoSchema bool) {
		vectorRepo = &fakeVectorRepo{}
		config := &config.WeaviateConfig{
			Config: config.Config{
				AutoSchema: config.AutoSchema{
					Enabled: autoSchema,
				},
			},
		}
		locks := &fakeLocks{}
		schemaManager := &fakeSchemaManager{
			GetSchemaResponse: schema,
		}
		logger, _ := test.NewNullLogger()
		authorizer := mocks.NewMockAuthorizer()
		modulesProvider := getFakeModulesProvider()
		manager = NewBatchManager(vectorRepo, modulesProvider, locks,
			schemaManager, config, logger, authorizer, nil)
	}

	reset := func() {
		resetAutoSchema(false)
	}
	ctx := context.Background()

	reset()

	t.Run("with invalid input", func(t *testing.T) {
		tests := []struct {
			input         *models.BatchDelete
			expectedError string
		}{
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
					Match: &models.BatchDeleteMatch{
						Class: "SomeClass",
						Where: &models.WhereFilter{
							Path:      []string{"some", "path"},
							Operator:  "Equal",
							ValueText: ptString("value"),
						},
					},
				},
				expectedError: "validate: failed to get class: SomeClass",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
					Match: &models.BatchDeleteMatch{
						Class: "Foo",
						Where: &models.WhereFilter{
							Path:      []string{"some"},
							Operator:  "Equal",
							ValueText: ptString("value"),
						},
					},
				},
				expectedError: "validate: invalid where filter: no such prop with name 'some' found in class 'Foo' " +
					"in the schema. Check your schema files for which properties in this class are available",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
				},
				expectedError: "validate: empty match clause",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
					Match: &models.BatchDeleteMatch{
						Class: "",
					},
				},
				expectedError: "validate: empty match.class clause",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
					Match: &models.BatchDeleteMatch{
						Class: "Foo",
					},
				},
				expectedError: "validate: empty match.where clause",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString(verbosity.OutputVerbose),
					Match: &models.BatchDeleteMatch{
						Class: "Foo",
						Where: &models.WhereFilter{
							Path:      []string{},
							Operator:  "Equal",
							ValueText: ptString("name"),
						},
					},
				},
				expectedError: "validate: failed to parse where filter: invalid where filter: field 'path': must have at least one element",
			},
			{
				input: &models.BatchDelete{
					DryRun: ptBool(false),
					Output: ptString("Simplified Chinese"),
					Match: &models.BatchDeleteMatch{
						Class: "Foo",
						Where: &models.WhereFilter{
							Path:      []string{"name"},
							Operator:  "Equal",
							ValueText: ptString("value"),
						},
					},
				},
				expectedError: "validate: invalid output: \"Simplified Chinese\", possible values are: \"minimal\", \"verbose\"",
			},
		}

		for _, test := range tests {
			_, err := manager.DeleteObjects(ctx, nil, test.input.Match, test.input.DeletionTimeUnixMilli, test.input.DryRun, test.input.Output, nil, "")
			assert.Equal(t, test.expectedError, err.Error())
		}
	})
}

func ptBool(b bool) *bool {
	return &b
}

func ptString(s string) *string {
	return &s
}
