// Code generated by candi v1.3.1.

package repository

import (
	"context"
	"monorepo/services/line-chatbot/internal/modules/event/domain"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"pkg.agungdp.dev/candi/candihelper"
	"pkg.agungdp.dev/candi/candishared"
	"pkg.agungdp.dev/candi/tracer"
)

type eventRepoMongo struct {
	readDB, writeDB *mongo.Database
	collection      string
}

// NewEventRepoMongo mongo repo constructor
func NewEventRepoMongo(readDB, writeDB *mongo.Database) EventRepository {
	return &eventRepoMongo{
		readDB, writeDB, "events",
	}
}

func (r *eventRepoMongo) FindHello(ctx context.Context) (string, error) {
	trace := tracer.StartTrace(ctx, "EventRepoMongo:FindHello")
	defer trace.Finish()

	return "Hello from repo mongo layer", nil
}

func (r *eventRepoMongo) FindAll(ctx context.Context, filter *candishared.Filter) <-chan candishared.Result {
	output := make(chan candishared.Result)
	go func() {
		defer close(output)

		findOptions := options.Find()

		var sort = 1
		if filter.Sort == "desc" {
			sort = -1
		}
		if filter.OrderBy != "" {
			findOptions.SetSort(map[string]int{filter.OrderBy: sort})
		}
		findOptions.SetLimit(int64(filter.Limit))
		findOptions.SetSkip(int64(filter.Offset))

		cursor, err := r.readDB.Collection(r.collection).Find(ctx, bson.M{}, findOptions)
		if err != nil {
			output <- candishared.Result{Error: err}
			return
		}

		var results []domain.Event
		for cursor.Next(ctx) {
			var res domain.Event
			if err := cursor.Decode(&res); err != nil {
				output <- candishared.Result{Error: err}
				return
			}
			results = append(results, res)
		}

		output <- candishared.Result{Data: results}
	}()
	return output
}

func (r *eventRepoMongo) Count(ctx context.Context, filter *candishared.Filter) <-chan int {
	output := make(chan int)
	go func() {
		defer close(output)

		count, _ := r.readDB.Collection(r.collection).CountDocuments(ctx, bson.M{})
		output <- int(count)
	}()
	return output
}

func (r *eventRepoMongo) Save(ctx context.Context, data *domain.Event) <-chan error {
	output := make(chan error)
	go tracer.WithTraceFunc(ctx, "EventRepo:Save", func(ctx context.Context, tags map[string]interface{}) {
		defer close(output)
		var err error

		tracer.Log(ctx, "data", data)

		if data.ID.IsZero() {
			data.ID = primitive.NewObjectID()
			_, err = r.readDB.Collection(r.collection).InsertOne(ctx, data)
		} else {
			opt := options.UpdateOptions{
				Upsert: candihelper.ToBoolPtr(true),
			}
			_, err = r.readDB.Collection(r.collection).UpdateOne(ctx,
				bson.M{
					"_id": data.ID,
				},
				bson.M{
					"$set": data,
				}, &opt)
		}

		output <- err
	})
	return output
}
