package tool

import (
	"context"
	"errors"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"gitlab.fjmaimaimai.com/allied-creation/su-micro/pkg/transaction"
	"reflect"
)

type LazyLoadService[K comparable, T any] struct {
	FoundMap    map[K]T // 已找到对象Map
	NotFoundMap map[K]T // 未找到对象Map
	load        func(context.Context, transaction.Conn, K) (T, error)
}

func NewLazyLoadService[K comparable, T any](load func(context.Context, transaction.Conn, K) (T, error)) *LazyLoadService[K, T] {
	return &LazyLoadService[K, T]{
		FoundMap:    map[K]T{},
		NotFoundMap: map[K]T{},
		load:        load,
	}
}

func (s *LazyLoadService[K, T]) Load(ctx context.Context, conn transaction.Conn, k K) (T, error) {
	if isEmptyOrZeroValue(k) {
		var t T
		return t, fmt.Errorf("empty key ‘%v’", k)
	}
	if v, ok := s.NotFoundMap[k]; ok {
		return v, sqlx.ErrNotFound
	}
	if v, ok := s.FoundMap[k]; ok {
		return v, nil
	}
	if v, err := s.load(ctx, conn, k); err != nil {
		if errors.Is(err, sqlx.ErrNotFound) {
			s.NotFoundMap[k] = v
		}
		return v, err
	} else {
		s.FoundMap[k] = v
		return v, nil
	}
}

func isEmptyOrZeroValue(i interface{}) bool {
	if i == nil {
		return true // 如果接口为空,返回true
	}
	// 使用反射判断接口的值是否为零值
	v := reflect.ValueOf(i)
	switch v.Kind() {
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	default:
		return false // 其他类型默认不为空
	}
}