package repository

import (
	"context"
	"errors"
	"time"

	"backend/pkg/member/domain/entity"
	"backend/pkg/member/domain/repository"

	GIDLib "code.30cm.net/digimon/library-go/utils/invited_code"

	"backend/pkg/library/mongo"

	"github.com/zeromicro/go-zero/core/stores/mon"
	"go.mongodb.org/mongo-driver/v2/bson"
	mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

type AutoIDRepositoryParam struct {
	Conf   *mongo.Conf
	DBOpts []mon.Option
}

type AutoIDRepository struct {
	DB       mongo.DocumentDBUseCase
	UIDTrans GIDLib.ConvertUseCase
}

func NewAutoIDRepository(param AutoIDRepositoryParam) repository.AutoIDRepository {
	e := entity.AutoID{}
	documentDB, err := mongo.NewDocumentDB(param.Conf, e.CollectionName(), param.DBOpts...)
	if err != nil {
		panic(err)
	}

	return &AutoIDRepository{
		DB:       documentDB,
		UIDTrans: GIDLib.MustConverter(10, 8, GIDLib.ConvertTable),
	}
}

func (repo *AutoIDRepository) Insert(ctx context.Context, data *entity.AutoID) error {
	if data.ID.IsZero() {
		now := time.Now().UTC().UnixNano()
		data.ID = bson.NewObjectID()
		data.CreateAt = &now
		data.UpdateAt = &now
	}
	_, err := repo.DB.GetClient().InsertOne(ctx, data)

	return err
}

func (repo *AutoIDRepository) FindOne(ctx context.Context, id string) (*entity.AutoID, error) {
	oid, err := bson.ObjectIDFromHex(id)
	if err != nil {
		return nil, ErrInvalidObjectID
	}

	var data entity.AutoID

	err = repo.DB.GetClient().FindOne(ctx, &data, bson.M{"_id": oid})
	switch {
	case err == nil:
		return &data, nil
	case errors.Is(err, mon.ErrNotFound):
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (repo *AutoIDRepository) Update(ctx context.Context, data *entity.AutoID) (*mongodriver.UpdateResult, error) {
	now := time.Now().UTC().UnixNano()
	data.UpdateAt = &now

	res, err := repo.DB.GetClient().UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})

	return res, err
}

func (repo *AutoIDRepository) Delete(ctx context.Context, id string) (int64, error) {
	oid, err := bson.ObjectIDFromHex(id)
	if err != nil {
		return 0, ErrInvalidObjectID
	}

	res, err := repo.DB.GetClient().DeleteOne(ctx, bson.M{"_id": oid})

	return res, err
}

func (repo *AutoIDRepository) Inc(ctx context.Context, data *entity.AutoID) error {
	// 定義查詢的條件
	filter := bson.M{"name": "auto_id"}

	// 定義更新的操作，包括自增和更新時間
	update := bson.M{
		"$inc": bson.M{"counter": 1},                            // 自增 counter
		"$set": bson.M{"updateAt": time.Now().UTC().UnixNano()}, // 設置 updateAt 為當前時間
	}
	// 使用 FindOneAndUpdate 並將結果解碼到 data 中
	err := repo.DB.GetClient().FindOneAndUpdate(ctx, &data, filter, update,
		options.FindOneAndUpdate().SetUpsert(true),
		options.FindOneAndUpdate().SetReturnDocument(options.After))

	return err
}

func (repo *AutoIDRepository) Index20241226001UP(ctx context.Context) (*mongodriver.Cursor, error) {
	// db.count.createIndex({ "name": 1 }, { unique: true });
	repo.DB.PopulateIndex(ctx, "name", 1, true)

	return repo.DB.GetClient().Indexes().List(ctx)
}

func (repo *AutoIDRepository) GetUIDFromNum(num int64) (string, error) {
	return repo.UIDTrans.EncodeFromNum(num)
}

func (repo *AutoIDRepository) GetNumFromUID(uid string) (int64, error) {
	return repo.UIDTrans.DecodeFromCode(uid)
}
