package cachex

import (
	"fmt"
	"github.com/tiptok/gocomm/pkg/cache"
	"github.com/tiptok/gocomm/pkg/log"
	"gitlab.fjmaimaimai.com/mmm-go/godevp/pkg/application/factory"
	"gitlab.fjmaimaimai.com/mmm-go/godevp/pkg/domain"
	"gitlab.fjmaimaimai.com/mmm-go/godevp/pkg/infrastructure/dao"
	"regexp"
	"strings"
)

//redis key
func UserRoleAccessCacheKey(userId int64) string {
	return fmt.Sprintf("godevp:user:access:%v", userId)
}

type CacheService struct {
}

func (svr *CacheService) CacheUserAccess(userId int64) func() (interface{}, error) {
	return func() (interface{}, error) {
		var (
			transactionContext, _ = factory.CreateTransactionContext(nil)
			UserRepository, _     = factory.CreateUserRepository(transactionContext)
			RoleAccessDao, _      = dao.NewRoleAccessDao(transactionContext)
			AccessRepository, _   = factory.CreateAccessRepository(transactionContext)
			err                   error
		)
		if err = transactionContext.StartTransaction(); err != nil {
			log.Error(err)
			return nil, err
		}
		defer func() {
			transactionContext.RollbackTransaction()
		}()

		var user *domain.User
		if user, err = UserRepository.FindOne(map[string]interface{}{"id": userId}); err != nil {
			return []*domain.Access{}, nil
		}
		accessIds, _ := RoleAccessDao.GetRoleAccess(user.Roles...)
		if len(accessIds) == 0 {
			return []*domain.Access{}, nil
		}
		_, accesses, _ := AccessRepository.Find(map[string]interface{}{"inAccessIds": accessIds})
		err = transactionContext.CommitTransaction()
		return accesses, nil
	}
}
func (svr *CacheService) ValidUserAccess(userId int64, object string, method string) (result bool, err error) {
	var userAccess []*domain.Access
	defer func() {
		log.Info(fmt.Sprintf("ValidUserAccess user:%v object:%v method:%v result:%v", userId, object, method, result))
	}()
	cache.GetObject(UserRoleAccessCacheKey(userId), &userAccess, 3600, svr.CacheUserAccess(userId))
	for i := range userAccess {
		if KeyMatch3(object, userAccess[i].Object) && KeyEqual(userAccess[i].Action, method) {
			result = true
			return
		}
	}
	return
}

// KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}"
func KeyMatch3(key1 string, key2 string) bool {
	key2 = strings.Replace(key2, "/*", "/.*", -1)

	re := regexp.MustCompile(`\{[^/]+\}`)
	key2 = re.ReplaceAllString(key2, "$1[^/]+$2")

	return RegexMatch(key1, "^"+key2+"$")
}

// KeyEqual  case key1='*' or key1=' '   result=true
func KeyEqual(key1 string, key2 string) bool {
	if key1 == "*" {
		return true
	}
	if len(key1) == 0 {
		return true
	}
	key1 = strings.ToLower(strings.TrimSpace(key1))
	key2 = strings.ToLower(strings.TrimSpace(key2))
	return strings.EqualFold(key1, key2)
}

// RegexMatch determines whether key1 matches the pattern of key2 in regular expression.
func RegexMatch(key1 string, key2 string) bool {
	res, err := regexp.MatchString(key2, key1)
	if err != nil {
		panic(err)
	}
	return res
}