package authz

import (
	"fmt"
	"sync"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	genericoptions "github.com/pingcap-inc/pinsights/internal/pkg/options"
)

var (
	enforcer *casbin.Enforcer
	once     sync.Once
)

func GetCasbinEnforcer(opts *genericoptions.MySQLOptions) (*casbin.Enforcer, error) {
	if opts == nil && enforcer == nil {
		return nil, fmt.Errorf("failed to get casbin enforcer")
	}

	var err error
	var e *casbin.Enforcer
	once.Do(func() {
		dsName := fmt.Sprintf("%s:%s@tcp(%s)/", opts.Username, opts.Password, opts.Host)
		a, _ := gormadapter.NewAdapter("mysql", dsName, "p7s_app")
		m := genModel()
		e, err = casbin.NewEnforcer(*m, a)

		if err == nil {
			e.LoadPolicy()
		}

		enforcer = e
	})

	if enforcer == nil || err != nil {
		return nil, fmt.Errorf("failed to get casbin enforcer, enforcer: %+v, error: %w", *enforcer, err)
	}

	return enforcer, nil
}

func genModel() *model.Model {
	m := model.NewModel()
	m.AddDef("r", "r", "sub, obj, act")
	m.AddDef("p", "p", "sub, obj, act")
	m.AddDef("g", "g", "_, _")
	m.AddDef("g", "g2", "_, _")
	m.AddDef("e", "e", "some(where (p.eft == allow))")
	m.AddDef("m", "m", "(g(r.sub, p.sub) || g2(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)")
	return &m
}
