// Package dict
// @Link  https://github.com/bufanyun/hotgo
// @Copyright  Copyright (c) 2023 HotGo CLI
// @Author  Ms <133814250@qq.com>
// @License  https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dict

import (
	"context"
	"fmt"
	"hotgo/internal/model"
	"sync"
)

// FuncDict 方法字典，实现本接口即可使用内置方法字典
type FuncDict func(ctx context.Context) (res []*model.Option, err error)

type FuncOption struct {
	Id    int64           // 字典ID，由系统自动生成
	Key   string          // 字典选项key
	Label string          // 字典选项标签名称
	Fun   FuncDict        // 字典方法
	Cache bool            // 是否缓存数据选项
	Opts  []*model.Option // 缓存的数据选项
	sync.Mutex
}

var (
	funcOptions = make(map[string]*FuncOption)
	fLock       sync.Mutex
)

// GetAllFunc 获取所有方法字典
func GetAllFunc() map[string]*FuncOption {
	return funcOptions
}

// RegisterFunc 注册方法字典选项
func RegisterFunc(key, label string, fun FuncDict, cache ...bool) {
	fLock.Lock()
	defer fLock.Unlock()

	if len(key) == 0 {
		panic("字典key不能为空")
	}

	if _, ok := funcOptions[key]; ok {
		panic(fmt.Sprintf("重复注册方法选项:%v", key))
	}

	isCache := false
	if len(cache) > 0 {
		isCache = cache[0]
	}

	funcOptions[key] = &FuncOption{
		Id:    GenIdHash(key, FuncId),
		Key:   key,
		Label: label,
		Fun:   fun,
		Cache: isCache,
		Opts:  nil,
	}
}

// SaveFunc 更新方法字典选项
func SaveFunc(key, label string, fun FuncDict, cache ...bool) {
	fLock.Lock()
	defer fLock.Unlock()
	delete(funcOptions, key)
	RegisterFunc(key, label, fun, cache...)
}

// ClearFuncCache 清理指定方法缓存选项
func ClearFuncCache(key string) (err error) {
	fun, ok := funcOptions[key]
	if !ok {
		err = NotExistKeyError
		return
	}

	fun.Lock()
	defer fun.Unlock()

	if fun.Opts != nil {
		fun.Opts = nil
	}
	return
}

// GetFuncOptions 获取指定方法字典的数据选项
func GetFuncOptions(ctx context.Context, key string) (res []*model.Option, err error) {
	fun, ok := funcOptions[key]
	if !ok {
		err = NotExistKeyError
		return
	}
	return LoadFuncOptions(ctx, fun)
}

// LoadFuncOptions 加载指定方法字典的数据选项
func LoadFuncOptions(ctx context.Context, fun *FuncOption) (res []*model.Option, err error) {
	if fun.Cache && fun.Opts != nil {
		res = fun.Opts
		return
	}

	fun.Lock()
	defer fun.Unlock()

	if fun.Cache && fun.Opts != nil {
		res = fun.Opts
		return
	}

	res, err = fun.Fun(ctx)
	if err != nil {
		return nil, err
	}

	for k := range res {
		res[k].Type = fun.Key
	}

	if fun.Cache {
		fun.Opts = res
	}
	return
}
