// Code generated by candi v1.3.1.

package repository

import (
	"context"
	"errors"
	"time"

	"monorepo/services/notification-service/internal/modules/push-notif/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/logger"
	"pkg.agungdp.dev/candi/tracer"
)

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

// NewPushNotifRepoMongo mongo repo constructor
func NewPushNotifRepoMongo(readDB, writeDB *mongo.Database) PushNotifRepository {
	return &pushnotifRepoMongo{
		readDB, writeDB, "topics",
	}
}

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

	return "Hello from repo mongo layer", nil
}

func (r *pushnotifRepoMongo) Save(ctx context.Context, data *domain.Topic) <-chan error {
	output := make(chan error)

	go func() {
		defer close(output)

		var err error

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

		if err != nil {
			output <- err
			return
		}
	}()

	return output
}

func (r *pushnotifRepoMongo) FindTopic(ctx context.Context, where domain.Topic) <-chan candishared.Result {
	output := make(chan candishared.Result)

	go func() {
		defer close(output)
		var data domain.Topic

		bsonWhere := make(primitive.M)
		if where.ID != "" {
			bsonWhere["_id"], _ = primitive.ObjectIDFromHex(where.ID)
		}
		if where.Name != "" {
			bsonWhere["name"] = where.Name
		}

		err := r.readDB.Collection(r.collection).FindOne(ctx, bsonWhere).Decode(&data)
		if err != nil {
			output <- candishared.Result{Error: err}
			return
		}

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

	return output
}

func (r *pushnotifRepoMongo) RemoveSubscriber(ctx context.Context, subscriber *domain.Subscriber) <-chan error {
	output := make(chan error)

	go func() {
		defer close(output)

		bsonWhere := make(primitive.M)
		bsonWhere["name"] = subscriber.Topic
		bsonWhere["subscribers.id"] = subscriber.ID

		_, err := r.writeDB.Collection(r.collection).UpdateOne(ctx,
			bsonWhere,
			primitive.M{
				"$set": primitive.M{"subscribers.$.isActive": false},
			}, &options.UpdateOptions{
				Upsert: candihelper.ToBoolPtr(true),
			})
		if err != nil {
			logger.LogE(err.Error())
			output <- err
			return
		}

	}()

	return output
}

func (r *pushnotifRepoMongo) FindSubscriber(ctx context.Context, topicName string, subscriber *domain.Subscriber) <-chan candishared.Result {
	output := make(chan candishared.Result)

	go func() {
		defer close(output)
		var data domain.Topic

		bsonWhere := make(primitive.M)
		bsonWhere["name"] = topicName
		bsonWhere["subscribers.id"] = subscriber.ID

		err := r.readDB.Collection(r.collection).FindOne(ctx, bsonWhere).Decode(&data)
		if err != nil {
			logger.LogE(err.Error())
			output <- candishared.Result{Error: err}
			return
		}

		if len(data.Subscribers) == 0 {
			output <- candishared.Result{Error: errors.New("Not found")}
			return
		}

		output <- candishared.Result{Data: data.Subscribers[0]}
	}()

	return output
}
