package testcases

import (
	"math"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"go.uber.org/zap"

	"github.com/milvus-io/milvus/client/v2/column"
	"github.com/milvus-io/milvus/client/v2/entity"
	"github.com/milvus-io/milvus/client/v2/index"
	client "github.com/milvus-io/milvus/client/v2/milvusclient"
	"github.com/milvus-io/milvus/pkg/v2/log"
	"github.com/milvus-io/milvus/tests/go_client/common"
	hp "github.com/milvus-io/milvus/tests/go_client/testcases/helper"
)

func TestInsertDefault(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	for _, autoID := range [2]bool{false, true} {
		// create collection
		cp := hp.NewCreateCollectionParams(hp.Int64Vec)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(autoID), hp.TNewSchemaOption())

		// insert
		columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
		pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt)
		vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *columnOpt)
		insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(vecColumn)
		if !autoID {
			insertOpt.WithColumns(pkColumn)
		}
		insertRes, err := mc.Insert(ctx, insertOpt)
		common.CheckErr(t, err, true)
		if !autoID {
			common.CheckInsertResult(t, pkColumn, insertRes)
		}
	}
}

func TestInsertDefaultPartition(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	for _, autoID := range [2]bool{false, true} {
		// create collection
		cp := hp.NewCreateCollectionParams(hp.Int64Vec)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(autoID), hp.TNewSchemaOption())

		// create partition
		parName := common.GenRandomString("par", 4)
		err := mc.CreatePartition(ctx, client.NewCreatePartitionOption(schema.CollectionName, parName))
		common.CheckErr(t, err, true)

		// insert
		columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
		pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt)
		vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *columnOpt)
		insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(vecColumn)
		if !autoID {
			insertOpt.WithColumns(pkColumn)
		}
		insertRes, err := mc.Insert(ctx, insertOpt.WithPartition(parName))
		common.CheckErr(t, err, true)
		if !autoID {
			common.CheckInsertResult(t, pkColumn, insertRes)
		}
	}
}

func TestInsertVarcharPkDefault(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	for _, autoID := range [2]bool{false, true} {
		// create collection
		cp := hp.NewCreateCollectionParams(hp.VarcharBinary)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(autoID).TWithMaxLen(20), hp.TNewSchemaOption())

		// insert
		columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
		pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeVarChar, *columnOpt)
		vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeBinaryVector, *columnOpt)
		insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(vecColumn)
		if !autoID {
			insertOpt.WithColumns(pkColumn)
		}
		insertRes, err := mc.Insert(ctx, insertOpt)
		common.CheckErr(t, err, true)
		if !autoID {
			common.CheckInsertResult(t, pkColumn, insertRes)
		}
	}
}

// test insert data into collection that has all scala fields
func TestInsertAllFieldsData(t *testing.T) {
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	for _, dynamic := range [2]bool{false, true} {
		// create collection
		cp := hp.NewCreateCollectionParams(hp.AllFields)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(dynamic))

		// insert
		insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName)
		columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
		for _, field := range schema.Fields {
			if field.DataType == entity.FieldTypeArray {
				columnOpt.TWithElementType(field.ElementType)
			}
			_column := hp.GenColumnData(common.DefaultNb, field.DataType, *columnOpt)
			insertOpt.WithColumns(_column)
		}
		if dynamic {
			insertOpt.WithColumns(hp.GenDynamicColumnData(0, common.DefaultNb)...)
		}
		insertRes, errInsert := mc.Insert(ctx, insertOpt)
		common.CheckErr(t, errInsert, true)
		pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt)
		common.CheckInsertResult(t, pkColumn, insertRes)

		// flush and check row count
		flushTak, _ := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
		err := flushTak.Await(ctx)
		common.CheckErr(t, err, true)

		// check collection stats
		stats, err := mc.GetCollectionStats(ctx, client.NewGetCollectionStatsOption(schema.CollectionName))
		common.CheckErr(t, err, true)
		require.Equal(t, map[string]string{common.RowCount: strconv.Itoa(common.DefaultNb)}, stats)
	}
}

// test insert dynamic data with column
func TestInsertDynamicExtraColumn(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// create collection
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(true))

	// insert without dynamic field
	insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName)
	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)

	for _, field := range schema.Fields {
		_column := hp.GenColumnData(common.DefaultNb, field.DataType, *columnOpt)
		insertOpt.WithColumns(_column)
	}
	insertRes, errInsert := mc.Insert(ctx, insertOpt)
	common.CheckErr(t, errInsert, true)
	require.Equal(t, common.DefaultNb, int(insertRes.InsertCount))

	// insert with dynamic field
	insertOptDynamic := client.NewColumnBasedInsertOption(schema.CollectionName)
	columnOpt.TWithStart(common.DefaultNb)
	for _, fieldType := range hp.GetAllScalarFieldType() {
		if fieldType == entity.FieldTypeArray {
			columnOpt.TWithElementType(entity.FieldTypeInt64).TWithMaxCapacity(2)
		}
		_column := hp.GenColumnData(common.DefaultNb, fieldType, *columnOpt)
		insertOptDynamic.WithColumns(_column)
	}
	insertOptDynamic.WithColumns(hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *columnOpt))
	insertRes2, errInsert2 := mc.Insert(ctx, insertOptDynamic)
	common.CheckErr(t, errInsert2, true)
	require.Equal(t, common.DefaultNb, int(insertRes2.InsertCount))

	// index
	it, _ := mc.CreateIndex(ctx, client.NewCreateIndexOption(schema.CollectionName, common.DefaultFloatVecFieldName, index.NewSCANNIndex(entity.COSINE, 32, false)))
	err := it.Await(ctx)
	common.CheckErr(t, err, true)

	// load
	lt, _ := mc.LoadCollection(ctx, client.NewLoadCollectionOption(schema.CollectionName))
	err = lt.Await(ctx)
	common.CheckErr(t, err, true)

	// query
	res, _ := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter("int64 == 3000").WithOutputFields("*"))
	common.CheckOutputFields(t, []string{common.DefaultFloatVecFieldName, common.DefaultInt64FieldName, common.DefaultDynamicFieldName}, res.Fields)
	for _, c := range res.Fields {
		log.Debug("data", zap.Any("data", c.FieldData()))
	}
}

func TestInsertFp16OrBf16VectorsWithFp32Vector(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
	fp16VecField := entity.NewField().WithName(common.DefaultFloat16VecFieldName).WithDataType(entity.FieldTypeFloat16Vector).WithDim(common.DefaultDim)
	bf16VecField := entity.NewField().WithName(common.DefaultBFloat16VecFieldName).WithDataType(entity.FieldTypeBFloat16Vector).WithDim(common.DefaultDim)

	// create collection
	collName := common.GenRandomString(prefix, 6)
	schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(fp16VecField).WithField(bf16VecField)
	err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
	common.CheckErr(t, err, true)

	// prepare data
	int64Column := hp.GenColumnData(100, entity.FieldTypeInt64, *hp.TNewDataOption())
	fp16VecColumn := hp.GenColumnDataWithFp32VecConversion(100, entity.FieldTypeFloat16Vector, *hp.TNewDataOption().TWithDim(128))
	bf16VecColumn := hp.GenColumnDataWithFp32VecConversion(100, entity.FieldTypeBFloat16Vector, *hp.TNewDataOption().TWithDim(128))
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, int64Column, fp16VecColumn, bf16VecColumn))
	common.CheckErr(t, err, true)
}

// test insert array column with empty data
func TestInsertEmptyArray(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VecArray)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim).TWithMaxCapacity(0)
	insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName)
	for _, field := range schema.Fields {
		if field.DataType == entity.FieldTypeArray {
			columnOpt.TWithElementType(field.ElementType)
		}
		_column := hp.GenColumnData(common.DefaultNb, field.DataType, *columnOpt)
		insertOpt.WithColumns(_column)
	}

	_, err := mc.Insert(ctx, insertOpt)
	common.CheckErr(t, err, true)
}

func TestInsertArrayDataTypeNotMatch(t *testing.T) {
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// share field and data
	int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
	vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)

	int64Column := hp.GenColumnData(100, entity.FieldTypeInt64, *hp.TNewDataOption())
	vecColumn := hp.GenColumnData(100, entity.FieldTypeFloatVector, *hp.TNewDataOption().TWithDim(128))
	for _, eleType := range hp.GetAllArrayElementType() {
		collName := common.GenRandomString(prefix, 6)
		arrayField := entity.NewField().WithName("array").WithDataType(entity.FieldTypeArray).WithElementType(eleType).WithMaxCapacity(100).WithMaxLength(100)

		// create collection
		schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(arrayField)
		err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
		common.CheckErr(t, err, true)

		// prepare data
		columnType := entity.FieldTypeInt64
		if eleType == entity.FieldTypeInt64 {
			columnType = entity.FieldTypeBool
		}
		arrayColumn := hp.GenColumnData(100, entity.FieldTypeArray, *hp.TNewDataOption().TWithElementType(columnType).TWithFieldName("array"))
		_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, int64Column, vecColumn, arrayColumn))
		common.CheckErr(t, err, false, "insert data does not match")
	}
}

func TestInsertArrayDataCapacityExceed(t *testing.T) {
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// share field and data
	int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
	vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)

	int64Column := hp.GenColumnData(100, entity.FieldTypeInt64, *hp.TNewDataOption())
	vecColumn := hp.GenColumnData(100, entity.FieldTypeFloatVector, *hp.TNewDataOption().TWithDim(128))
	for _, eleType := range hp.GetAllArrayElementType() {
		collName := common.GenRandomString(prefix, 6)
		arrayField := entity.NewField().WithName("array").WithDataType(entity.FieldTypeArray).WithElementType(eleType).WithMaxCapacity(common.TestCapacity).WithMaxLength(100)

		// create collection
		schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(arrayField)
		err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
		common.CheckErr(t, err, true)

		// insert array data capacity > field.MaxCapacity
		arrayColumn := hp.GenColumnData(100, entity.FieldTypeArray, *hp.TNewDataOption().TWithElementType(eleType).TWithFieldName("array").TWithMaxCapacity(common.TestCapacity * 2))
		_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, int64Column, vecColumn, arrayColumn))
		common.CheckErr(t, err, false, "array length exceeds max capacity")
	}
}

// test insert not exist collection or not exist partition
func TestInsertNotExist(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// insert data into not exist collection
	intColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *hp.TNewDataOption())
	_, err := mc.Insert(ctx, client.NewColumnBasedInsertOption("notExist", intColumn))
	common.CheckErr(t, err, false, "can't find collection")

	// insert data into not exist partition
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *hp.TNewDataOption().TWithDim(common.DefaultDim))
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, intColumn, vecColumn).WithPartition("aaa"))
	common.CheckErr(t, err, false, "partition not found")
}

// test insert data columns len, order mismatch fields
func TestInsertColumnsMismatchFields(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// column data
	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
	intColumn := hp.GenColumnData(100, entity.FieldTypeInt64, *columnOpt)
	floatColumn := hp.GenColumnData(100, entity.FieldTypeFloat, *columnOpt)
	vecColumn := hp.GenColumnData(100, entity.FieldTypeFloatVector, *columnOpt)

	// insert
	collName := schema.CollectionName

	// len(column) < len(fields)
	_, errInsert := mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, intColumn))
	common.CheckErr(t, errInsert, false, "has no corresponding fieldData pass in: invalid parameter")

	// len(column) > len(fields)
	_, errInsert2 := mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, intColumn, vecColumn, vecColumn))
	common.CheckErr(t, errInsert2, false, "duplicated column")

	//
	_, errInsert3 := mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, intColumn, floatColumn, vecColumn))
	common.CheckErr(t, errInsert3, false, "does not exist in collection")

	// order(column) != order(fields)
	_, errInsert4 := mc.Insert(ctx, client.NewColumnBasedInsertOption(collName, vecColumn, intColumn))
	common.CheckErr(t, errInsert4, true)
}

// test insert with columns which has different len
func TestInsertColumnsDifferentLen(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// column data
	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
	intColumn := hp.GenColumnData(100, entity.FieldTypeInt64, *columnOpt)
	vecColumn := hp.GenColumnData(200, entity.FieldTypeFloatVector, *columnOpt)

	// len(column) < len(fields)
	_, errInsert := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, intColumn, vecColumn))
	common.CheckErr(t, errInsert, false, "column size not match")
}

func TestInsertAutoIdPkData(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// create collection
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(true), hp.TNewSchemaOption())

	// insert
	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
	pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt)
	vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *columnOpt)
	insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(vecColumn, pkColumn)

	_, err := mc.Insert(ctx, insertOpt)
	common.CheckErr(t, err, false, "more fieldData has pass in")
}

// test insert invalid column: empty column or dim not match
func TestInsertInvalidColumn(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	// create collection
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// insert with empty column data
	pkColumn := column.NewColumnInt64(common.DefaultInt64FieldName, []int64{})
	vecColumn := hp.GenColumnData(100, entity.FieldTypeFloatVector, *hp.TNewDataOption())

	_, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, pkColumn, vecColumn))
	common.CheckErr(t, err, false, "need long int array][actual=got nil]")

	// insert with empty vector data
	vecColumn2 := column.NewColumnFloatVector(common.DefaultFloatVecFieldName, common.DefaultDim, [][]float32{})
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, pkColumn, vecColumn2))
	common.CheckErr(t, err, false, "num_rows should be greater than 0")

	// insert with vector data dim not match
	vecColumnDim := column.NewColumnFloatVector(common.DefaultFloatVecFieldName, common.DefaultDim-8, [][]float32{})
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, pkColumn, vecColumnDim))
	common.CheckErr(t, err, false, "vector dim 120 not match collection definition")
}

// test insert invalid column: empty column or dim not match
func TestInsertColumnVarcharExceedLen(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	// create collection
	varcharMaxLen := 10
	cp := hp.NewCreateCollectionParams(hp.VarcharBinary)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithMaxLen(int64(varcharMaxLen)), hp.TNewSchemaOption())

	// insert with empty column data
	varcharValues := make([]string, 0, 100)
	for i := 0; i < 100; i++ {
		_value := common.GenRandomString("", varcharMaxLen+1)
		varcharValues = append(varcharValues, _value)
	}
	pkColumn := column.NewColumnVarChar(common.DefaultVarcharFieldName, varcharValues)
	vecColumn := hp.GenColumnData(100, entity.FieldTypeBinaryVector, *hp.TNewDataOption())

	_, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, pkColumn, vecColumn))
	common.CheckErr(t, err, false, "length of varchar field varchar exceeds max length")
}

// test insert sparse vector
func TestInsertSparseData(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// insert sparse data
	columnOpt := hp.TNewDataOption()
	pkColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt)
	columns := []column.Column{
		pkColumn,
		hp.GenColumnData(common.DefaultNb, entity.FieldTypeVarChar, *columnOpt),
		hp.GenColumnData(common.DefaultNb, entity.FieldTypeSparseVector, *columnOpt.TWithSparseMaxLen(common.DefaultDim)),
	}
	inRes, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, columns...))
	common.CheckErr(t, err, true)
	common.CheckInsertResult(t, pkColumn, inRes)
}

func TestInsertSparseDataMaxDim(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// insert sparse data
	columnOpt := hp.TNewDataOption()
	pkColumn := hp.GenColumnData(1, entity.FieldTypeInt64, *columnOpt)
	varcharColumn := hp.GenColumnData(1, entity.FieldTypeVarChar, *columnOpt)

	// sparse vector with max dim
	positions := []uint32{0, math.MaxUint32 - 10, math.MaxUint32 - 1}
	values := []float32{0.453, 5.0776, 100.098}
	sparseVec, err := entity.NewSliceSparseEmbedding(positions, values)
	common.CheckErr(t, err, true)

	sparseColumn := column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, []entity.SparseEmbedding{sparseVec})
	inRes, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, pkColumn, varcharColumn, sparseColumn))
	common.CheckErr(t, err, true)
	common.CheckInsertResult(t, pkColumn, inRes)
}

// empty spare vector can't be searched, but can be queried
func TestInsertReadSparseEmptyVector(t *testing.T) {
	// invalid sparse vector: positions >= uint32
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
	prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())
	prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
	prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))

	// insert data column
	columnOpt := hp.TNewDataOption()
	data := []column.Column{
		hp.GenColumnData(1, entity.FieldTypeInt64, *columnOpt),
		hp.GenColumnData(1, entity.FieldTypeVarChar, *columnOpt),
	}

	//  sparse vector: empty position and values
	sparseVec, err := entity.NewSliceSparseEmbedding([]uint32{}, []float32{})
	common.CheckErr(t, err, true)
	data = append(data, column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, []entity.SparseEmbedding{sparseVec}))
	insertRes, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, data...))
	common.CheckErr(t, err, true)
	require.EqualValues(t, 1, insertRes.InsertCount)

	// query and check vector is empty
	resQuery, err := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithLimit(10).WithOutputFields(common.DefaultSparseVecFieldName).WithConsistencyLevel(entity.ClStrong))
	common.CheckErr(t, err, true)
	require.Equal(t, 1, resQuery.ResultCount)
	log.Info("sparseVec", zap.Any("data", resQuery.GetColumn(common.DefaultSparseVecFieldName).(*column.ColumnSparseFloatVector).Data()))
	common.EqualColumn(t, resQuery.GetColumn(common.DefaultSparseVecFieldName), column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, []entity.SparseEmbedding{sparseVec}))
}

func TestInsertSparseInvalidVector(t *testing.T) {
	// invalid sparse vector: len(positions) != len(values)
	positions := []uint32{1, 10}
	values := []float32{0.4, 5.0, 0.34}
	_, err := entity.NewSliceSparseEmbedding(positions, values)
	common.CheckErr(t, err, false, "invalid sparse embedding input, positions shall have same number of values")

	// invalid sparse vector: positions >= uint32
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// insert data column
	columnOpt := hp.TNewDataOption()
	data := []column.Column{
		hp.GenColumnData(1, entity.FieldTypeInt64, *columnOpt),
		hp.GenColumnData(1, entity.FieldTypeVarChar, *columnOpt),
	}
	// invalid sparse vector: position > (maximum of uint32 - 1)
	positions = []uint32{math.MaxUint32}
	values = []float32{0.4}
	sparseVec, err := entity.NewSliceSparseEmbedding(positions, values)
	common.CheckErr(t, err, true)
	data = append(data, column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, []entity.SparseEmbedding{sparseVec}))
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, data...))
	common.CheckErr(t, err, false, "invalid index in sparse float vector: must be less than 2^32-1")
}

func TestInsertSparseVectorSamePosition(t *testing.T) {
	// invalid sparse vector: positions >= uint32
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption())

	// insert data column
	columnOpt := hp.TNewDataOption()
	data := []column.Column{
		hp.GenColumnData(1, entity.FieldTypeInt64, *columnOpt),
		hp.GenColumnData(1, entity.FieldTypeVarChar, *columnOpt),
	}
	// invalid sparse vector: position > (maximum of uint32 - 1)
	sparseVec, err := entity.NewSliceSparseEmbedding([]uint32{2, 10, 2}, []float32{0.4, 0.5, 0.6})
	common.CheckErr(t, err, true)
	data = append(data, column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, []entity.SparseEmbedding{sparseVec}))
	_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, data...))
	common.CheckErr(t, err, false, "unsorted or same indices in sparse float vector")
}

/******************
 Test insert rows
******************/

// test insert rows enable or disable dynamic field
func TestInsertDefaultRows(t *testing.T) {
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	for _, autoId := range []bool{false, true} {
		cp := hp.NewCreateCollectionParams(hp.Int64Vec)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(autoId), hp.TNewSchemaOption())
		log.Info("fields", zap.Any("FieldNames", schema.Fields))

		// insert rows
		rows := hp.GenInt64VecRows(common.DefaultNb, false, autoId, *hp.TNewDataOption())
		log.Info("rows data", zap.Any("rows[8]", rows[8]))
		ids, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...))
		common.CheckErr(t, err, true)
		if !autoId {
			int64Values := make([]int64, 0, common.DefaultNb)
			for i := 0; i < common.DefaultNb; i++ {
				int64Values = append(int64Values, int64(i+1))
			}
			common.CheckInsertResult(t, column.NewColumnInt64(common.DefaultInt64FieldName, int64Values), ids)
		}
		require.Equal(t, ids.InsertCount, int64(common.DefaultNb))

		// flush and check row count
		flushTask, errFlush := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
		common.CheckErr(t, errFlush, true)
		errFlush = flushTask.Await(ctx)
		common.CheckErr(t, errFlush, true)

		// check collection stats
		stats, err := mc.GetCollectionStats(ctx, client.NewGetCollectionStatsOption(schema.CollectionName))
		common.CheckErr(t, err, true)
		require.Equal(t, map[string]string{common.RowCount: strconv.Itoa(common.DefaultNb)}, stats)
	}
}

func TestInsertDefaultRowsWithKeepAutoIDPk(t *testing.T) {
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(true), hp.TNewSchemaOption())
	log.Info("fields", zap.Any("FieldNames", schema.Fields))
	err := mc.AlterCollectionProperties(ctx, client.NewAlterCollectionPropertiesOption(schema.CollectionName).WithProperty("allow_insert_auto_id", true))
	common.CheckErr(t, err, true)

	// insert rows
	rows := hp.GenInt64VecRows(common.DefaultNb, false, false, *hp.TNewDataOption())
	log.Info("rows data", zap.Any("rows[8]", rows[8]))
	ids, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...).WithKeepAutoIDPk(true))
	common.CheckErr(t, err, true)
	int64Values := make([]int64, 0, common.DefaultNb)
	for i := 0; i < common.DefaultNb; i++ {
		int64Values = append(int64Values, int64(i+1))
	}
	common.CheckInsertResult(t, column.NewColumnInt64(common.DefaultInt64FieldName, int64Values), ids)
	require.Equal(t, ids.InsertCount, int64(common.DefaultNb))

	// flush and check row count
	flushTask, errFlush := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
	common.CheckErr(t, errFlush, true)
	errFlush = flushTask.Await(ctx)
	common.CheckErr(t, errFlush, true)

	// check collection stats
	stats, err := mc.GetCollectionStats(ctx, client.NewGetCollectionStatsOption(schema.CollectionName))
	common.CheckErr(t, err, true)
	require.Equal(t, map[string]string{common.RowCount: strconv.Itoa(common.DefaultNb)}, stats)
}

// test insert rows enable or disable dynamic field
func TestInsertAllFieldsRows(t *testing.T) {
	t.Skip("https://github.com/milvus-io/milvus/issues/33459")
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	for _, enableDynamicField := range [2]bool{true, false} {
		cp := hp.NewCreateCollectionParams(hp.AllFields)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(enableDynamicField))
		log.Info("fields", zap.Any("FieldNames", schema.Fields))

		// insert rows
		rows := hp.GenAllFieldsRows(common.DefaultNb, false, *hp.TNewDataOption())
		log.Debug("", zap.Any("row[0]", rows[0]))
		log.Debug("", zap.Any("row", rows[1]))
		ids, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...))
		common.CheckErr(t, err, true)

		int64Values := make([]int64, 0, common.DefaultNb)
		for i := 0; i < common.DefaultNb; i++ {
			int64Values = append(int64Values, int64(i))
		}
		common.CheckInsertResult(t, column.NewColumnInt64(common.DefaultInt64FieldName, int64Values), ids)

		// flush and check row count
		flushTask, errFlush := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
		common.CheckErr(t, errFlush, true)
		errFlush = flushTask.Await(ctx)
		common.CheckErr(t, errFlush, true)
	}
}

// test insert rows enable or disable dynamic field
func TestInsertVarcharRows(t *testing.T) {
	t.Skip("https://github.com/milvus-io/milvus/issues/33457")
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	for _, autoId := range []bool{true} {
		cp := hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec)
		_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithAutoID(autoId))
		log.Info("fields", zap.Any("FieldNames", schema.Fields))

		// insert rows
		rows := hp.GenInt64VarcharSparseRows(common.DefaultNb, false, autoId, *hp.TNewDataOption().TWithSparseMaxLen(1000))
		ids, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...))
		common.CheckErr(t, err, true)

		int64Values := make([]int64, 0, common.DefaultNb)
		for i := 0; i < common.DefaultNb; i++ {
			int64Values = append(int64Values, int64(i))
		}
		common.CheckInsertResult(t, column.NewColumnInt64(common.DefaultInt64FieldName, int64Values), ids)

		// flush and check row count
		flushTask, errFlush := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
		common.CheckErr(t, errFlush, true)
		errFlush = flushTask.Await(ctx)
		common.CheckErr(t, errFlush, true)
	}
}

func TestInsertSparseRows(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
	sparseField := entity.NewField().WithName(common.DefaultSparseVecFieldName).WithDataType(entity.FieldTypeSparseVector)
	collName := common.GenRandomString("insert", 6)
	schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(sparseField)
	err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
	common.CheckErr(t, err, true)

	// prepare rows
	rows := make([]interface{}, 0, common.DefaultNb)

	// BaseRow generate insert rows
	for i := 0; i < common.DefaultNb; i++ {
		vec := common.GenSparseVector(500)
		// log.Info("", zap.Any("SparseVec", vec))
		baseRow := hp.BaseRow{
			Int64:     int64(i + 1),
			SparseVec: vec,
		}
		rows = append(rows, &baseRow)
	}
	ids, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...))
	common.CheckErr(t, err, true)

	int64Values := make([]int64, 0, common.DefaultNb)
	for i := 0; i < common.DefaultNb; i++ {
		int64Values = append(int64Values, int64(i+1))
	}
	common.CheckInsertResult(t, column.NewColumnInt64(common.DefaultInt64FieldName, int64Values), ids)

	// flush and check row count
	flushTask, errFlush := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
	common.CheckErr(t, errFlush, true)
	errFlush = flushTask.Await(ctx)
	common.CheckErr(t, errFlush, true)
}

// test field name: pk, row json name: int64
func TestInsertRowFieldNameNotMatch(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	// create collection with pk name: pk
	vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
	int64Field := entity.NewField().WithName("pk").WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
	collName := common.GenRandomString(prefix, 6)
	schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField)
	err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
	common.CheckErr(t, err, true)

	// insert rows, with json key name: int64
	rows := hp.GenInt64VecRows(10, false, false, *hp.TNewDataOption())
	_, errInsert := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rows...))
	common.CheckErr(t, errInsert, false, "fieldSchema(pk) has no corresponding fieldData pass in")
}

// test field name: pk, row json name: int64
func TestInsertRowMismatchFields(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)

	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithDim(8), hp.TNewSchemaOption())

	// rows fields < schema fields
	rowsLess := make([]interface{}, 0, 10)
	for i := 1; i < 11; i++ {
		row := hp.BaseRow{
			Int64: int64(i),
		}
		rowsLess = append(rowsLess, row)
	}
	_, errInsert := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsLess...))
	common.CheckErr(t, errInsert, false, "[expected=need float vector][actual=got nil]")

	/*
		// extra fields
		t.Log("https://github.com/milvus-io/milvus/issues/33487")
		rowsMore := make([]interface{}, 0, 10)
		for i := 1; i< 11; i++ {
			row := hp.BaseRow{
				Int64: int64(i),
				Int32: int32(i),
				FloatVec: common.GenFloatVector(8),
			}
			rowsMore = append(rowsMore, row)
		}
		log.Debug("Row data", zap.Any("row[0]", rowsMore[0]))
		_, errInsert = mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsMore...))
		common.CheckErr(t, errInsert, false, "")
	*/

	// rows order != schema order
	rowsOrder := make([]interface{}, 0, 10)
	for i := 1; i < 11; i++ {
		row := hp.BaseRow{
			FloatVec: common.GenFloatVector(8),
			Int64:    int64(i),
		}
		rowsOrder = append(rowsOrder, row)
	}
	log.Debug("Row data", zap.Any("row[0]", rowsOrder[0]))
	_, errInsert = mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsOrder...))
	common.CheckErr(t, errInsert, true)
}

func TestInsertDisableAutoIDRow(t *testing.T) {
	/*
		autoID: false
		- pass pk value -> insert success
		- no pk value -> error
	*/
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(false), hp.TNewSchemaOption().TWithAutoID(false))

	// pass pk value
	rowsWithPk := hp.GenInt64VecRows(10, false, false, *hp.TNewDataOption())
	idsWithPk, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsWithPk...))
	common.CheckErr(t, err, true)
	require.Contains(t, idsWithPk.IDs.(*column.ColumnInt64).Data(), rowsWithPk[0].(*hp.BaseRow).Int64)

	// no pk value -> now error
	type tmpRow struct {
		FloatVec []float32 `json:"floatVec,omitempty" milvus:"name:floatVec"`
	}
	rowsWithoutPk := make([]interface{}, 0, 10)

	// BaseRow generate insert rows
	for i := 0; i < 10; i++ {
		baseRow := tmpRow{
			FloatVec: common.GenFloatVector(common.DefaultDim),
		}
		rowsWithoutPk = append(rowsWithoutPk, &baseRow)
	}
	_, err1 := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsWithoutPk...))
	common.CheckErr(t, err1, false, "fieldSchema(int64) has no corresponding fieldData pass in")
}

func TestInsertEnableAutoIDRow(t *testing.T) {
	/*
		autoID: true
		- pass pk value -> ignore passed value and write back auto-gen pk
		- no pk value -> insert success
	*/
	t.Parallel()
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(true), hp.TNewSchemaOption().TWithAutoID(true))

	// pass pk value -> ignore passed pks
	rowsWithPk := hp.GenInt64VecRows(10, false, false, *hp.TNewDataOption())
	log.Debug("origin first rowsWithPk", zap.Any("rowsWithPk", rowsWithPk[0].(*hp.BaseRow)))
	idsWithPk, err := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsWithPk...))
	log.Info("write back rowsWithPk", zap.Any("rowsWithPk", rowsWithPk[0].(*hp.BaseRow)))
	common.CheckErr(t, err, true)
	require.Contains(t, idsWithPk.IDs.(*column.ColumnInt64).Data(), rowsWithPk[0].(*hp.BaseRow).Int64)

	// no pk value -> now error
	rowsWithoutPk := make([]interface{}, 0, 10)
	type tmpRow struct {
		FloatVec []float32 `json:"floatVec,omitempty" milvus:"name:floatVec"`
	}

	// BaseRow generate insert rows
	for i := 0; i < 10; i++ {
		baseRow := tmpRow{
			FloatVec: common.GenFloatVector(common.DefaultDim),
		}
		rowsWithoutPk = append(rowsWithoutPk, &baseRow)
	}

	idsWithoutPk, err1 := mc.Insert(ctx, client.NewRowBasedInsertOption(schema.CollectionName, rowsWithoutPk...))
	common.CheckErr(t, err1, true)
	require.Equal(t, 10, int(idsWithoutPk.InsertCount))
}

func TestFlushRate(t *testing.T) {
	ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
	mc := hp.CreateDefaultMilvusClient(ctx, t)
	// create collection
	cp := hp.NewCreateCollectionParams(hp.Int64Vec)
	_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, cp, hp.TNewFieldsOption().TWithAutoID(true), hp.TNewSchemaOption())

	// insert
	columnOpt := hp.TNewDataOption().TWithDim(common.DefaultDim)
	vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *columnOpt)
	insertOpt := client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(vecColumn)
	_, err := mc.Insert(ctx, insertOpt)
	common.CheckErr(t, err, true)

	cnt := 10
	errs := make([]error, cnt)
	wg := &sync.WaitGroup{}
	wg.Add(cnt)
	for i := 0; i < cnt; i++ {
		go func(i int) {
			defer wg.Done()
			_, err := mc.Flush(ctx, client.NewFlushOption(schema.CollectionName))
			errs[i] = err
		}(i)
	}
	wg.Wait()

	errCnt := 0
	for _, err := range errs {
		if err != nil {
			common.CheckErr(t, err, false, "request is rejected by grpc RateLimiter middleware, please retry later: rate limit exceeded")
			errCnt++
		}
	}
	require.NotZero(t, errCnt)
}
