package privilege

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	xormadapter "github.com/casbin/xorm-adapter/v2"
	"github.com/civet148/log"
	"github.com/civet148/sqlca/v2"
	_ "github.com/go-sql-driver/mysql"
	"intent-system/pkg/dal/dao"
	"intent-system/pkg/dal/models"
	"strings"
)

//var CasRule *casbin.Enforcer

const DefaultCasbinModel = `
[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 = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && r.act == p.act || r.sub == "root"
`

type CasbinOption struct {
	Model string
	DSN   string
}

type CasbinRule struct {
	cas          *casbin.Enforcer
	privilegeDAO *dao.PrivilegeDAO
}

func NewCasbinRule(opt CasbinOption) *CasbinRule {
	db, err := sqlca.NewEngine(opt.DSN)
	if err != nil {
		log.Panic("connect to database error [%s]", err.Error())
		return nil
	}
	cas := &CasbinRule{
		privilegeDAO: dao.NewPrivilegeDAO(db),
		cas:          newCasbin(opt.DSN, opt.Model),
	}

	return cas.initPrivileges()
}

func newCasbin(strDSN, strModel string) (cas *casbin.Enforcer) {
	var err error
	if strings.HasPrefix(strDSN, "mysql://") {
		strDSN, err = sqlca.Url2MySql(strDSN)
		if err != nil {
			panic(err.Error())
		}
	}
	log.Infof("DSN [%s]", strDSN)
	// 要使用自己定义的数据库,最后的true很重要.默认为false,使用缺省的数据库名casbin,不存在则创建，表不需要自己创建默认为casbin_rule
	a, err := xormadapter.NewAdapter("mysql", strDSN, true)
	if err != nil {
		log.Fatalf("error: model: %s", err)
	}
	if strModel == "" {
		strModel = DefaultCasbinModel
	}
	m, err := model.NewModelFromString(strModel)
	if err != nil {
		log.Fatalf("error: model: %s", err)
	}

	cas, err = casbin.NewEnforcer(m, a)
	if err != nil {
		log.Fatalf("error: model: %s", err)
	}
	//从DB加载策略
	err = cas.LoadPolicy()
	if err != nil {
		log.Fatalf("casbin load policy error [%s]", err.Error())
		return nil
	}
	return cas
}

// [][]string默认位数
const (
	roleIndex = 0
	pathIndex = 1
	authIndex = 2
)

func (m *CasbinRule) initPrivileges() *CasbinRule {
	var err error
	for _, p := range TotalPrivileges() {
		_, err = m.privilegeDAO.Upsert(&models.PrivilegeDO{
			Name:       p.Name,
			Label:      p.Label,
			Path:       p.Path,
			Children:   p.Children,
			IsInherent: true,
			Remark:     "inherent privilege (DO NOT EDIT OR DELETE)",
			Deleted:    false,
		})
		if err != nil {
			log.Panic(err.Error())
		}
	}
	return m
}

// 编辑权限（除账户和角色类其他菜单都可以增删改查)
func (m *CasbinRule) EditPrivileges() (authorities []string) {
	return []string{
		UserAccess,
		RoleAccess,
		NewsAccess,
		NewsAdd,
		NewsDelete,
		NewsEdit,
		QA_Access,
		QA_Add,
		QA_Delete,
		QA_Edit,
		SubAccess,
		SubAdd,
		SubEdit,
		TagAccess,
		TagAdd,
		TagDelete,
		TagEdit,
		CustomerAccess,
	}
}

// 普通管理账户权限(所有菜单均只有查看权限)
func (m *CasbinRule) AccessPrivileges() (authorities []string) {
	return []string{
		UserAccess,
		RoleAccess,
		NewsAccess,
		QA_Access,
		SubAccess,
		TagAccess,
		CustomerAccess,
	}
}

func (m *CasbinRule) TotalPrivileges() (authorities []string) {
	privileges := TotalPrivileges()
	for _, p := range privileges {
		as := recursiveAuthorities(p.Children)
		if len(as) != 0 {
			authorities = append(authorities, as...)
		}
	}
	return authorities
}

// 检查用户+URL+角色是否有访问权限
func (m *CasbinRule) Enforce(strUserName, strRequestURI string, privilege string) (ok bool, err error) {
	return m.cas.Enforce(strUserName, strRequestURI, string(privilege))
}

// 添加角色权限
func (m *CasbinRule) AddRoleAuthority(role string, accessPath string, authority string) {
	_, err := m.cas.AddPolicy(role, accessPath, authority)
	if err != nil {
		log.Errorf("add role [%s] authority [%s] path [%s] error [%s]", role, authority, accessPath, err.Error())
		return
	}
}

// 获取角色权限
func (m *CasbinRule) GetRoleAuthority(role string) (authority []string) {
	var authorityList = make([]string, 0)
	list := m.cas.GetPermissionsForUser(role)
	for _, vlist := range list {
		if len(vlist) > authIndex {
			authorityList = append(authorityList, vlist[authIndex])
		}
	}
	return authorityList
}

// 角色权限继承 roleA 继承/获取 roleB权限
func (m *CasbinRule) InheritRoleAuthority(roleA, roleB string) {
	// 获取roleB权限
	list := m.cas.GetPermissionsForUser(roleB)
	for _, listV := range list {
		if len(listV) >= authIndex {
			m.cas.AddPolicy(roleA, listV[pathIndex], listV[authIndex])
		}
	}
}

// 角色更名时，用户继承新角色,并删除旧角色
func (m *CasbinRule) GetUsersForRole(role string) (users []string, err error) {
	return m.cas.GetUsersForRole(role)
}

// 角色更名时，用户继承新角色,并删除旧角色
func (m *CasbinRule) InheritUserRole(roleA, roleB string) {
	// 获取具有角色的用户
	res, err := m.cas.GetUsersForRole(roleB)
	if err != nil {
		log.Errorf("get users for role:%s error:%s", roleB, err.Error())
		return
	}
	for _, user := range res {
		m.AddUserRole(user, roleA)
		m.DeleteUserRole(user, roleB)
	}
}

// 删除角色权限
func (m *CasbinRule) DeleteRoleAuthority(role string, accessPath string, authority string) {
	if ok, _ := m.cas.RemovePolicy(role, accessPath, authority); !ok {
		fmt.Println("role authority doesn't exit!")
	} else {
		fmt.Println("role authority delete success")
	}
}

// 删除一个角色
func (m *CasbinRule) DeleteRole(role string) {
	m.cas.DeleteRole(role)
}

// 获取用户权限列表
func (m *CasbinRule) GetUserRoleList(userName string) (roleList []string) {
	roleList = make([]string, 0)
	// 获取用户角色
	res, err := m.cas.GetRolesForUser(userName)
	if err != nil {
		log.Error(err.Error())
		return roleList
	}
	for _, role := range res {
		// 获取角色权限
		list := m.cas.GetPermissionsForUser(role)
		for _, vlist := range list {
			if len(vlist) >= authIndex {
				roleList = append(roleList, vlist[authIndex])
			}
		}
	}
	return m.removeDuplicateElement(roleList)
}

// 为用户添加角色
func (m *CasbinRule) AddUserRole(userName string, role string) {
	if ok, _ := m.cas.AddRoleForUser(userName, role); !ok {
		log.Infof("role hadn't exit:%s", role)
	} else {
		log.Infof("add role success!")
	}
}

// 删除用户角色
func (m *CasbinRule) DeleteUserRole(userName, role string) {
	ok, _ := m.cas.DeleteRoleForUser(userName, role)
	if ok {
		log.Infof("delete role:%s for user:%s success", role, userName)
	} else {
		log.Infof("delete role:%s for user:%s failed!", role, userName)
	}
}

// 删除用户所有角色
func (m *CasbinRule) DeleteAllRoleForUser(userName string) {
	if ok, _ := m.cas.DeleteRolesForUser(userName); !ok {
		log.Infof("delete all user role failed:%s", userName)
	} else {
		log.Infof("delete all user role success!")
	}
}

// 删除一个用户
func (m *CasbinRule) DeleteUser(userName string) {
	m.cas.DeleteUser(userName)
}

// 权限列表去重
func (m *CasbinRule) removeDuplicateElement(authList []string) []string {
	result := make([]string, 0, len(authList))
	temp := map[string]struct{}{}
	for _, item := range authList {
		if _, ok := temp[item]; !ok {
			temp[item] = struct{}{}
			result = append(result, item)
		}
	}
	return result
}

// 查询所有权限信息
func (m *CasbinRule) GetAllPrivileges() (privileges models.TreePrivilege) {
	return TotalPrivileges()
}

func (m *CasbinRule) GetPrivilegePath(auth string) (path string) {
	privileges := m.GetAllPrivileges()
	for _, p := range privileges {
		if p.Name == auth {
			return p.Path
		}
		path = recursiveSearchPath(auth, p.Children)
		if path != "" {
			return path
		}
	}
	return path
}

func recursiveSearchPath(auth string, children models.TreePrivilege) (path string) {
	for _, p := range children {
		if auth == p.Name {
			path = p.Path
		} else {
			if p.Children != nil {
				path = recursiveSearchPath(auth, p.Children)
				if path != "" {
					return path
				}
			}
		}
	}
	return path
}

func recursiveAuthorities(children models.TreePrivilege) (authorities []string) {
	for _, p := range children {
		if p.Name != "" {
			authorities = append(authorities, p.Name)
		}
		if p.Children != nil {
			as := recursiveAuthorities(p.Children)
			if len(as) != 0 {
				authorities = append(authorities, as...)
			}
		}
	}
	return
}
