package mcache

import (
	"context"
	"encoding/json"
	"time"

	"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/database"
	"github.com/openimsdk/tools/log"
)

func getCache[V any](ctx context.Context, cache database.Cache, key string, expireTime time.Duration, fn func(ctx context.Context) (V, error)) (V, error) {
	getDB := func() (V, bool, error) {
		res, err := cache.Get(ctx, []string{key})
		if err != nil {
			var val V
			return val, false, err
		}
		var val V
		if str, ok := res[key]; ok {
			if json.Unmarshal([]byte(str), &val) != nil {
				return val, false, err
			}
			return val, true, nil
		}
		return val, false, nil
	}
	dbVal, ok, err := getDB()
	if err != nil {
		return dbVal, err
	}
	if ok {
		return dbVal, nil
	}
	lockValue, err := cache.Lock(ctx, key, time.Minute)
	if err != nil {
		return dbVal, err
	}
	defer func() {
		if err := cache.Unlock(ctx, key, lockValue); err != nil {
			log.ZError(ctx, "unlock cache key", err, "key", key, "value", lockValue)
		}
	}()
	dbVal, ok, err = getDB()
	if err != nil {
		return dbVal, err
	}
	if ok {
		return dbVal, nil
	}
	val, err := fn(ctx)
	if err != nil {
		return val, err
	}
	data, err := json.Marshal(val)
	if err != nil {
		return val, err
	}
	if err := cache.Set(ctx, key, string(data), expireTime); err != nil {
		return val, err
	}
	return val, nil
}
