package sysAuthRule

import (
	"context"
	"fmt"

	"kami/internal/dao"
	"kami/internal/model"
	"kami/internal/model/do"
	"kami/internal/model/entity"
	"kami/internal/service"
	"kami/utility/config"
	"kami/utility/utils"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/util/gconv"
)

//func init() {
//	service.RegisterSysAuthRule(New())
//}

func New() *sSysAuthRule {
	return &sSysAuthRule{}
}

type sSysAuthRule struct{}

func (s *sSysAuthRule) GetMenuListSearch(ctx context.Context, req *model.RuleSearchInput) (res []*model.SysAuthRuleInfoOutput, err error) {
	m := dao.V1SysAuthRule.Ctx(ctx).DB(config.GetDatabaseV1())
	if req.Title != "" {
		m = m.WhereLike(dao.V1SysAuthRule.Columns().Title, utils.OrmLike(req.Title))
	}
	if req.Component != "" {
		m = m.Where(dao.V1SysAuthRule.Columns().Component, utils.OrmLike(req.Component))
	}
	err = m.Fields(model.SysAuthRuleInfoOutput{}).OrderDesc(dao.V1SysAuthRule.Columns().Weigh).
		OrderAsc(dao.V1SysAuthRule.Columns().Id).Scan(&res)
	return
}

// GetIsMenuList 获取isMenu=0|1
func (s *sSysAuthRule) GetIsMenuList(ctx context.Context) ([]*model.SysAuthRuleInfoOutput, error) {
	list, err := s.GetMenuList(ctx)
	if err != nil {
		return nil, err
	}
	gList := make([]*model.SysAuthRuleInfoOutput, 0, len(list))
	for _, v := range list {
		if v.MenuType == 0 || v.MenuType == 1 {
			gList = append(gList, v)
		}
	}
	return gList, nil
}

// GetMenuList 获取所有菜单
func (s *sSysAuthRule) GetMenuList(ctx context.Context) (list []*model.SysAuthRuleInfoOutput, err error) {
	iList, err := s.getMenuListFromDb(ctx)
	if err != nil || iList == nil {
		return
	}
	err = gconv.Struct(iList, &list)
	return
}

// 从数据库获取所有菜单
func (s *sSysAuthRule) getMenuListFromDb(ctx context.Context) (value interface{}, err error) {
	var v []*model.SysAuthRuleInfoOutput
	// 从数据库获取
	err = dao.V1SysAuthRule.Ctx(ctx).
		Fields(model.SysAuthRuleInfoOutput{}).Order("weigh desc,id asc").Scan(&v)
	value = v
	return
}

// GetIsButtonList 获取所有按钮isMenu=2 菜单列表
func (s *sSysAuthRule) GetIsButtonList(ctx context.Context) ([]*model.SysAuthRuleInfoOutput, error) {
	list, err := s.GetMenuList(ctx)
	if err != nil {
		return nil, err
	}
	gList := make([]*model.SysAuthRuleInfoOutput, 0, len(list))
	for _, v := range list {
		if v.MenuType == 2 {
			gList = append(gList, v)
		}
	}
	return gList, nil
}

// Add 添加菜单
func (s *sSysAuthRule) Add(ctx context.Context, req *model.RuleAddReq) (err error) {
	if s.menuNameExists(ctx, req.Name, 0) {
		err = gerror.New("接口规则已经存在")
		return
	}
	err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 菜单数据
		ruleId, err := dao.V1SysAuthRule.Ctx(ctx).TX(tx).DB(config.GetDatabaseV1()).
			InsertAndGetId(do.V1SysAuthRule{
				Pid:       req.Pid,
				Name:      req.Name,
				Title:     req.Title,
				Icon:      req.Icon,
				Condition: req.Condition,
				Remark:    req.Remark,
				MenuType:  req.MenuType,
				Weigh:     req.Weigh,
				IsHide:    req.IsHide,
				Path:      req.Path,
				Component: req.Component,
				IsLink:    req.IsLink,
				IsIframe:  req.IsIframe,
				IsCached:  req.IsCached,
				Redirect:  req.Redirect,
				IsAffix:   req.IsAffix,
				LinkUrl:   req.LinkUrl,
			})
		if err != nil {
			return err
		}
		err = s.BindRoleRule(ctx, ruleId, req.Roles)
		return err
	})
	return
}

// 检查菜单规则是否存在
func (s *sSysAuthRule) menuNameExists(ctx context.Context, name string, id uint) bool {
	m := dao.V1SysAuthRule.Ctx(ctx).Where(dao.V1SysAuthRule.Columns().Name, name).DB(config.GetDatabaseV1())
	if id != 0 {
		m = m.WhereNot(dao.V1SysAuthRule.Columns().Id, id)
	}
	c, err := m.Fields(dao.V1SysAuthRule.Columns().Id).Limit(1).One()
	if err != nil {
		glog.Error(ctx, err)
		return false
	}
	return !c.IsEmpty()
}

// BindRoleRule 绑定角色权限
func (s *sSysAuthRule) BindRoleRule(ctx context.Context, ruleId interface{}, roleIds []uint) (err error) {
	casbinService := service.Casbin()
	enforcer, err := casbinService.CasbinEnforcer(ctx)
	if err != nil {
		return
	}
	for _, roleId := range roleIds {
		_, _ = enforcer.AddPolicy(fmt.Sprintf("%d", roleId), fmt.Sprintf("%d", ruleId), "All")
	}
	return
}

func (s *sSysAuthRule) Get(ctx context.Context, id uint) (rule *entity.V1SysAuthRule, err error) {
	err = dao.V1SysAuthRule.Ctx(ctx).WherePri(id).Scan(&rule)
	return
}

func (s *sSysAuthRule) GetMenuRoles(ctx context.Context, id uint) (roleIds []uint, err error) {
	casbinService := service.Casbin()
	enforcer, err := casbinService.CasbinEnforcer(ctx)
	if err != nil {
		return
	}
	policies, err := enforcer.GetFilteredNamedPolicy("p", 1, gconv.String(id))
	if err != nil {
		return
	}
	for _, policy := range policies {
		roleIds = append(roleIds, gconv.Uint(policy[0]))
	}
	return
}

func (s *sSysAuthRule) Update(ctx context.Context, req *model.RuleUpdateInput) (err error) {
	if s.menuNameExists(ctx, req.Name, req.Id) {
		err = gerror.New("接口规则已经存在")
		return
	}
	err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.V1SysAuthRule.Ctx(ctx).TX(tx).DB(config.GetDatabaseV1()).WherePri(req.Id).
			Update(do.V1SysAuthRule{
				Pid:       req.Pid,
				Name:      req.Name,
				Title:     req.Title,
				Icon:      req.Icon,
				Condition: req.Condition,
				Remark:    req.Remark,
				MenuType:  req.MenuType,
				Weigh:     req.Weigh,
				IsHide:    req.IsHide,
				Path:      req.Path,
				Component: req.Component,
				IsLink:    req.IsLink,
				IsIframe:  req.IsIframe,
				IsCached:  req.IsCached,
				Redirect:  req.Redirect,
				IsAffix:   req.IsAffix,
				LinkUrl:   req.LinkUrl,
			})
		if err != nil {
			return err
		}
		err = s.UpdateRoleRule(ctx, req.Id, req.Roles)
		return err
	})
	return
}

func (s *sSysAuthRule) UpdateRoleRule(ctx context.Context, ruleId uint, roleIds []uint) (err error) {
	casbinService := service.Casbin()
	enforcer, err := casbinService.CasbinEnforcer(ctx)
	if err != nil {
		return
	}
	// 删除旧权限
	_, err = enforcer.RemoveFilteredPolicy(1, gconv.String(ruleId))
	if err != nil {
		return
	}
	// 添加新权限
	roleIdsStrArr := gconv.Strings(roleIds)
	for _, v := range roleIdsStrArr {
		_, _ = enforcer.AddPolicy(v, gconv.String(ruleId), "All")
	}
	return
}

func (s *sSysAuthRule) GetMenuListTree(pid uint, list []*model.SysAuthRuleInfoOutput) []*model.SysAuthRuleTreeOutput {
	tree := make([]*model.SysAuthRuleTreeOutput, 0, len(list))
	for _, menu := range list {
		if menu.Pid == pid {
			t := &model.SysAuthRuleTreeOutput{
				SysAuthRuleInfoOutput: menu,
			}
			child := s.GetMenuListTree(menu.Id, list)
			if child != nil {
				t.Children = child
			}
			tree = append(tree, t)
		}
	}
	return tree
}

// DeleteMenuByIds 删除菜单
func (s *sSysAuthRule) DeleteMenuByIds(ctx context.Context, ids []int) (err error) {
	var list []*model.SysAuthRuleInfoOutput
	list, err = s.GetMenuList(ctx)
	if err != nil {
		return
	}
	childrenIds := make([]int, 0, len(list))
	for _, id := range ids {
		rules := s.FindSonByParentId(list, gconv.Uint(id))
		for _, child := range rules {
			childrenIds = append(childrenIds, gconv.Int(child.Id))
		}
	}
	ids = append(ids, childrenIds...)
	err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.V1SysAuthRule.Ctx(ctx).WhereIn(dao.V1SysAuthRule.Columns().Id, ids).Delete()
		if err != nil {
			return err
		}
		casbinService := service.Casbin()
		// 删除权限
		enforcer, err := casbinService.CasbinEnforcer(ctx)
		if err != nil {
			return err
		}
		for _, v := range ids {
			_, _ = enforcer.RemoveFilteredPolicy(1, gconv.String(v))
		}
		return err
	})
	return
}

func (s *sSysAuthRule) FindSonByParentId(list []*model.SysAuthRuleInfoOutput, pid uint) []*model.SysAuthRuleInfoOutput {
	children := make([]*model.SysAuthRuleInfoOutput, 0, len(list))
	for _, v := range list {
		if v.Pid == pid {
			children = append(children, v)
			fChildren := s.FindSonByParentId(list, v.Id)
			children = append(children, fChildren...)
		}
	}
	return children
}
