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
		)
		transactionContext.SetTransactionClose()
		if err = transactionContext.StartTransaction(); err != nil {
			log.Error(err)
			return nil, err
		}
		defer func() {
			transactionContext.RollbackTransaction()
		}()

		var user *domain.Users
		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) ValidUser(userId int64) (isAdmin bool, err error) {
	var (
		transactionContext, _ = factory.CreateTransactionContext(nil)
		UserRepository, _     = factory.CreateUserRepository(transactionContext)
	)
	transactionContext.SetTransactionClose()
	if err = transactionContext.StartTransaction(); err != nil {
		log.Error(err)
		return isAdmin, err
	}
	defer func() {
		transactionContext.RollbackTransaction()
	}()

	var user *domain.Users
	if user, err = UserRepository.FindOne(map[string]interface{}{"id": userId}); err != nil {
		return isAdmin, err
	}
	if user.AdminType == domain.UserAdmin {
		isAdmin = true
	}
	err = transactionContext.CommitTransaction()
	return isAdmin, nil
}
func (svr *CacheService) ValidUserAccess(userId int64, object string, method string) (result bool, err error) {
	var userAccess []*domain.Access
	var isAdmin bool
	defer func() {
		log.Info(fmt.Sprintf("ValidUserAccess user:%v object:%v method:%v result:%v", userId, object, method, result))
	}()
	if isAdmin, err = svr.ValidUser(userId); err != nil {
		return
	}
	//管理员有所有权限
	if isAdmin {
		result = true
		return
	}
	cache.GetObject(UserRoleAccessCacheKey(userId), &userAccess, 3600, svr.CacheUserAccess(userId))
	for i := range userAccess {
		if KeyMatch3(object, userAccess[i].Object) && ActionEqual(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}" ,"/foo" matches "/foo/*"
func KeyMatch3(key1 string, key2 string) bool {
	key2 = strings.Replace(key2, "/*", "/.*", -1)

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

func key1HasVersion(key1 string) string {
	vTags := []string{"v1", "v2", "v3", "v4"} //,"v5","v6","v7","v8","v9"
	for _, tag := range vTags {
		if index := strings.Index(key1, tag); index >= 0 {
			return key1[index+len(tag):]
		}
	}
	return key1
}

// KeyEqual  case key1='*' or key1=' '   result=true
func ActionEqual(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
}