package casbin

import (
	"sync"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"gorm.io/gorm"
)

var (
	syncedCachedEnforcer *casbin.SyncedCachedEnforcer
	once                 sync.Once
)

// InitCasbin 初始化Casbin
func InitCasbin(db *gorm.DB, modelPath string) (*casbin.SyncedCachedEnforcer, error) {
	var err error
	once.Do(func() {
		adapter, adapterErr := gormadapter.NewAdapterByDB(db)
		if adapterErr != nil {
			err = adapterErr
			return
		}

		var m model.Model
		if modelPath != "" {
			m, err = model.NewModelFromFile(modelPath)
		} else {
			// 默认RBAC模型
			m, err = model.NewModelFromString(`
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
`)
		}
		if err != nil {
			return
		}

		syncedCachedEnforcer, err = casbin.NewSyncedCachedEnforcer(m, adapter)
		if err != nil {
			return
		}
		syncedCachedEnforcer.SetExpireTime(60 * 60)
		_ = syncedCachedEnforcer.LoadPolicy()
	})
	return syncedCachedEnforcer, err
}

// GetEnforcer 获取Enforcer实例
func GetEnforcer() *casbin.SyncedCachedEnforcer {
	return syncedCachedEnforcer
}

// CheckPermission 检查权限
func CheckPermission(sub, obj, act string) (bool, error) {
	if syncedCachedEnforcer == nil {
		return false, nil
	}
	return syncedCachedEnforcer.Enforce(sub, obj, act)
}

// AddPolicy 添加策略
func AddPolicy(sub, obj, act string) (bool, error) {
	return syncedCachedEnforcer.AddPolicy(sub, obj, act)
}

// RemovePolicy 删除策略
func RemovePolicy(sub, obj, act string) (bool, error) {
	return syncedCachedEnforcer.RemovePolicy(sub, obj, act)
}

// GetPoliciesForUser 获取用户的所有策略
func GetPoliciesForUser(sub string) [][]string {
	policies, _ := syncedCachedEnforcer.GetFilteredPolicy(0, sub)
	return policies
}

// UpdateCasbinApi 更新API权限
func UpdateCasbinApi(oldPath, newPath, oldMethod, newMethod string) error {
	_, err := syncedCachedEnforcer.UpdatePolicy(
		[]string{"", oldPath, oldMethod},
		[]string{"", newPath, newMethod},
	)
	return err
}
