package mongoLogger

import (
	"Yimaru-Backend/internal/config"
	"context"
	"fmt"
	"time"

	"maps"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.uber.org/zap/zapcore"
)

type MongoCore struct {
	collection *mongo.Collection
	level      zapcore.Level
	fields     []zapcore.Field
	cfg        *config.Config
}

func NewMongoCore(uri, dbName, collectionName string, level zapcore.Level, cfg *config.Config) (zapcore.Core, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
	if err != nil {
		return nil, err
	}

	if err := client.Ping(ctx, nil); err != nil {
		return nil, fmt.Errorf("unable to connect to MongoDB: %w", err)
	}

	coll := client.Database(dbName).Collection(collectionName)
	return &MongoCore{
		collection: coll,
		level:      level,
		cfg:        cfg,
	}, nil
}

func (mc *MongoCore) Enabled(lvl zapcore.Level) bool {
	return lvl >= mc.level
}

func (mc *MongoCore) With(fields []zapcore.Field) zapcore.Core {
	clone := *mc
	clone.fields = append(clone.fields, fields...)
	return &clone
}

func (mc *MongoCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	if mc.Enabled(entry.Level) {
		return checkedEntry.AddCore(entry, mc)
	}
	return checkedEntry
}

func (mc *MongoCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
	logMap := make(map[string]interface{})
	enc := zapcore.NewMapObjectEncoder()

	for _, f := range append(mc.fields, fields...) {
		f.AddTo(enc)
	}

	maps.Copy(logMap, enc.Fields)

	doc := bson.M{
		"level":      entry.Level.String(),
		"message":    entry.Message,
		"timestamp":  entry.Time.UTC().Format(time.RFC3339Nano),
		"fields":     logMap,
		"caller":     entry.Caller.String(),
		"stacktrace": entry.Stack,
		"service":    mc.cfg.Service,
		"env":        mc.cfg.Env,
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := mc.collection.InsertOne(ctx, doc)
	return err
}

func (mc *MongoCore) Sync() error {
	return nil
}
