package api

import (
	"fmt" // "html/template"
	"reflect"

	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func (mongo *Mongo) InsertOneRx(options *Filter) *ObservableStruct {

	return Observable(func(observer *Subscriber) {
		if res, err := mongo.InsertOne(options); err != nil {
			observer.Err(err)
		} else {
			observer.Next(res)
		}
	})

}

func (_mongo *Mongo) FindManyRx(params *Filter) *ObservableStruct {

	var (
		ctx, collection = _mongo.GetContext(params)
		cursor          *mongo.Cursor
		err             error
	)

	return Observable(func(observer *Subscriber) {

		switch {
		case params.Pipeline != nil:

			// opts := options.Aggregate().SetBatchSize(int32(params.MaxResults))
			opts := options.Aggregate()

			if cursor, err = collection.Aggregate(ctx, params.Pipeline, opts); err != nil {
				err = errs.FromError(err)
				return
			}
		case params.Query != nil:
			findOptions := options.Find()
			findOptions.SetLimit(int64(params.MaxResults))

			if params.Sort != nil {
				findOptions.SetSort(params.Sort)
			}

			if params.Fields != nil {
				findOptions.SetProjection(params.Fields)
			}

			cursor, err = collection.Find(ctx, params.Query, findOptions)

			fmt.Println("erro no query")
		default:
			err = errs.Internal().Details(&errs.Detail{
				Message: "FindMany requires a Pipeline or Query param.",
			})
		}

		if err == nil {
			observer.Next(cursor)
		} else {
			observer.Next(err)
		}

	}).Pipe(
		RxMap(func(value interface{}) (interface{}, error) {
			var (
				cursor, _ = value.(*mongo.Cursor)
				elemp     reflect.Value
				err       error
			)
			defer cursor.Close(ctx)

			if params.Entities == nil {
				return nil, fmt.Errorf("Entities can't be nil")
			}

			entitiesValue := reflect.ValueOf(params.Entities)

			if entitiesValue.Kind() != reflect.Ptr || entitiesValue.Elem().Kind() != reflect.Slice {
				return nil, fmt.Errorf("Entities argument must be a slice address")
			}

			sliceVector := entitiesValue.Elem()
			sliceVector = sliceVector.Slice(0, sliceVector.Cap())
			typ := sliceVector.Type().Elem()

			// LogError(0, cursor.Err().Error())

			for cursor.Next(ctx) {
				fmt.Println("interate")
				elemp = reflect.New(typ)

				if err = cursor.Decode(elemp.Interface()); err != nil {
					return nil, err
				}
				sliceVector = reflect.Append(sliceVector, elemp.Elem())
			}

			entitiesValue.Elem().Set(sliceVector)

			return entitiesValue.Elem(), nil
		}),
	)

}
