// Code generated by candi v1.8.17.

package repository

import (
	"context"
	"errors"
	"time"

	"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"

	shareddomain "monorepo/services/auth-service/pkg/shared/domain"

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

type tokenRepoMongo struct {
	readDB, writeDB *mongo.Database
}

// NewTokenRepoMongo mongo repo constructor
func NewTokenRepoMongo(readDB, writeDB *mongo.Database) TokenRepository {
	return &tokenRepoMongo{
		readDB: readDB, writeDB: writeDB,
	}
}

func (r *tokenRepoMongo) FetchAll(ctx context.Context, filter *candishared.Filter) (data []shareddomain.Token, err error) {
	trace := tracer.StartTrace(ctx, "TokenRepoMongo:FetchAll")
	defer func() { trace.SetError(err); trace.Finish() }()
	ctx = trace.Context()

	query := bson.M{}
	findOptions := options.Find()
	if len(filter.OrderBy) > 0 {
		findOptions.SetSort(filter)
	}

	if !filter.ShowAll {
		findOptions.SetLimit(int64(filter.Limit))
		findOptions.SetSkip(int64(filter.Offset))
	}
	cur, err := r.readDB.Collection(shareddomain.Token{}.CollectionName()).Find(ctx, query, findOptions)
	if err != nil {
		return data, err
	}
	cur.All(ctx, &data)
	return
}

func (r *tokenRepoMongo) Find(ctx context.Context, data *shareddomain.Token) (err error) {
	trace := tracer.StartTrace(ctx, "TokenRepoMongo:Find")
	defer func() { trace.SetError(err); trace.Finish() }()
	ctx = trace.Context()

	bsonWhere := make(bson.M)
	if data.ID != "" {
		bsonWhere["_id"] = data.ID
	}
	if data.DeviceID != "" {
		bsonWhere["device_id"] = data.DeviceID
	}
	if data.UserID != "" {
		bsonWhere["user_id"] = data.UserID
	}
	if data.IsActive != nil {
		bsonWhere["is_active"] = *data.IsActive
	}
	if data.Token != "" {
		bsonWhere["token"] = data.Token
	}
	if data.RefreshToken != "" {
		bsonWhere["refresh_token"] = data.RefreshToken
	}

	if len(bsonWhere) == 0 {
		return errors.New("Data not found")
	}

	return r.readDB.Collection(data.CollectionName()).FindOne(ctx, bsonWhere).Decode(data)
}

func (r *tokenRepoMongo) Count(ctx context.Context, filter *candishared.Filter) int {
	trace := tracer.StartTrace(ctx, "TokenRepoMongo:Count")
	defer trace.Finish()

	query := bson.M{}
	count, err := r.readDB.Collection(shareddomain.Token{}.CollectionName()).CountDocuments(ctx, query)
	trace.SetError(err)
	return int(count)
}

func (r *tokenRepoMongo) Save(ctx context.Context, data *shareddomain.Token) (err error) {
	trace := tracer.StartTrace(ctx, "TokenRepoMongo:Save")
	defer func() { trace.SetError(err); trace.Finish() }()
	ctx = trace.Context()
	tracer.Log(ctx, "data", data)

	if data.CreatedAt.IsZero() {
		data.CreatedAt = time.Now()
	}
	data.ModifiedAt = time.Now()

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

	return
}
