package core

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"image"
	"image/png"
	"intent-system/pkg/config"
	"intent-system/pkg/dal/dao"
	"intent-system/pkg/dal/models"
	"intent-system/pkg/email"
	"intent-system/pkg/itypes"
	"intent-system/pkg/privilege"
	"intent-system/pkg/proto"
	"intent-system/pkg/utils"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/civet148/log"
	"github.com/civet148/sqlca/v2"
	"github.com/gin-gonic/gin"
)

const (
	InherentAdminName       = "admin"
	SystemName              = "system"
	InherentAdminPassword   = "c333e2df646d876661e4a13e86feba99" //Coeus@123456
	InherentAdminNameRemark = "inherent administrator"
	InherentAdminRoleRemark = "supper administrator role"

	InherentAdminRole  = "超级管理员"
	InherentEditRole   = "编辑者"
	InherentAccessRole = "访问者"
)

var inherentRoles = map[string]string{
	InherentAdminRole:  "administrator",
	InherentEditRole:   "editor",
	InherentAccessRole: "accessor",
}

var isInitialized bool

type PlatformCore struct {
	db            *sqlca.Engine
	cfg           *config.Config
	cas           *privilege.CasbinRule
	userDAO       *dao.UserDAO
	roleDAO       *dao.RoleDAO
	loginDAO      *dao.LoginDAO
	userRoleDAO   *dao.UserRoleDAO
	privilegeDAO  *dao.PrivilegeDAO
	dictionaryDAO *dao.DictionaryDAO
	inviteCodeDAO *dao.InviteCodeDAO
	customerDAO   *dao.CustomerDAO
}

func NewPlatformCore(cfg *config.Config, db *sqlca.Engine) *PlatformCore {
	cas := privilege.NewCasbinRule(privilege.CasbinOption{
		DSN:   cfg.DSN,
		Model: privilege.DefaultCasbinModel,
	})
	m := &PlatformCore{
		db:            db,
		cfg:           cfg,
		cas:           cas,
		userDAO:       dao.NewUserDAO(db),
		roleDAO:       dao.NewRoleDAO(db),
		loginDAO:      dao.NewLoginDAO(db),
		userRoleDAO:   dao.NewUserRoleDAO(db),
		privilegeDAO:  dao.NewPrivilegeDAO(db),
		dictionaryDAO: dao.NewDictionaryDAO(db),
		inviteCodeDAO: dao.NewInviteCodeDAO(db),
		customerDAO:   dao.NewCustomerDAO(db),
	}
	return m.initialize()
}

// initialize role and privilege of platform inherent
func (m *PlatformCore) initialize() *PlatformCore {
	if !isInitialized {
		m.initializeInherentRoles()
		m.initializeInherentAccount()
	}
	isInitialized = true
	return m
}

func (m *PlatformCore) initializeInherentRoles() {
	for role, alias := range inherentRoles {
		m.initializeInherentRole(role, alias)
	}
	m.initializeInherentPrivileges()
}

func (m *PlatformCore) initializeInherentRole(role, alias string) {
	var err error
	var exist bool

	if exist, err = m.roleDAO.CheckRoleExistByName(role); err != nil {
		log.Errorf(err.Error())
		return
	}

	if !exist {
		if _, err = m.roleDAO.Insert(&models.RoleDO{
			RoleName:   role,
			RoleAlias:  alias,
			IsInherent: true,
		}); err != nil {
			log.Errorf(err.Error())
			return
		}
	}
}

func (m *PlatformCore) initializeInherentPrivileges() {
	for role, alias := range inherentRoles {
		var authorities []string
		switch role {
		case InherentAdminRole:
			authorities = m.cas.TotalPrivileges()
		case InherentEditRole:
			authorities = m.cas.EditPrivileges()
		case InherentAccessRole:
			authorities = m.cas.AccessPrivileges()
		default:
			log.Warnf("no inherent privilege for role [%s] alias [%s]", role, alias)
			continue
		}
		for _, authority := range authorities {
			strPath := m.cas.GetPrivilegePath(authority)
			if strings.EqualFold(strPath, "") {
				log.Errorf("unknown authority [%s]", authority)
				continue
			}
			// 给角色授予权限
			m.cas.AddRoleAuthority(role, strPath, authority)
		}
	}
}

func (m *PlatformCore) initializeInherentAccount() {
	if ok, err := m.userDAO.CheckActiveUserByUserName(InherentAdminName); err != nil {
		log.Errorf("query by user name error [%s]", err.Error())
		return
	} else if ok {
		return
	}

	m.cas.AddUserRole(InherentAdminName, InherentAdminRole)

	var lastId int64
	var err error
	var strSalt = utils.GenerateSalt()
	do := &models.UserDO{
		UserName:   InherentAdminName,
		Password:   InherentAdminPassword,
		Salt:       strSalt,
		UserAlias:  InherentAdminName,
		IsAdmin:    true,
		Remark:     InherentAdminNameRemark,
		CreateUser: SystemName,
		EditUser:   SystemName,
		LoginTime:  utils.Now64(),
		Deleted:    false,
		State:      dao.UserState_Enabled,
	}
	if lastId, err = m.userDAO.Insert(do); err != nil {
		log.Errorf(err.Error())
		return
	}
	do.SetId(int32(lastId))
	if err = m.userRoleDAO.Insert(&models.UserRoleDO{
		UserName:   InherentAdminName,
		RoleName:   InherentAdminRole,
		CreateUser: SystemName,
		EditUser:   SystemName,
		Deleted:    false,
	}); err != nil {
		log.Errorf(err.Error())
		return
	}
}

func (m *PlatformCore) UserLogin(strUserName, strPassword, strIP string) (user *models.UserDO, code itypes.BizCode) {

	var err error
	if user, err = m.userDAO.SelectUserByName(strUserName); err != nil {
		log.Errorf("select [user name] from user table error [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_USER_OR_PASSWORD)
	}

	if user == nil || user.GetId() == 0 {
		if user, err = m.userDAO.SelectUserByEmail(strUserName); err != nil {
			log.Errorf("select [phone] from user table error [%s]", err.Error())
			return nil, itypes.NewBizCode(itypes.CODE_DATABASE_ERROR)
		}
		if user == nil || user.GetId() == 0 {
			log.Errorf("user name/email [%s] data not found in db", strUserName)
			return nil, itypes.NewBizCode(itypes.CODE_INVALID_USER_OR_PASSWORD)
		}
	}

	if user.State == dao.UserState_Disabled {
		log.Errorf("user name/email [%s] account was disabled", strUserName)
		return nil, itypes.NewBizCode(itypes.CODE_ACCOUNT_BANNED)
	}

	user.LoginIp = strIP
	user.LoginTime = time.Now().Unix()
	if err = m.userDAO.UpdateByName(user, models.USER_COLUMN_LOGIN_IP, models.USER_COLUMN_LOGIN_TIME); err != nil {
		log.Errorf("update user [%s] login ip error [%s]", strUserName, strIP)
		return nil, itypes.NewBizCodeDatabaseError()
	}

	if strPassword != user.Password {
		err = fmt.Errorf("user name [%s] password verify failed, password [%s] not match", strUserName, strPassword)
		log.Errorf(err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_USER_OR_PASSWORD)
	}
	_, _ = m.loginDAO.Insert(&models.LoginDO{
		UserId:    user.GetId(),
		LoginType: 0,
		LoginIp:   strIP,
		LoginAddr: "",
	})
	return
}

func (m *PlatformCore) GetUserRole(ctx *itypes.Context, strUserName string) (role *models.RoleDO) {
	var err error

	fmt.Println(ctx.UserName())
	if role, err = m.roleDAO.SelectUserRole(strUserName); err != nil {
		log.Errorf(err.Error())
		return
	}
	return
}

func (m *PlatformCore) GetUserRoleList(strUserName string) []string {
	return m.cas.GetUserRoleList(strUserName)
}

func (m *PlatformCore) CheckPrivilege(c *gin.Context, ctx *itypes.Context, authority string) (ok bool) {
	if authority == privilege.Null {
		return true
	}
	var err error
	if ok, err = m.cas.Enforce(ctx.UserName(), c.Request.URL.RequestURI(), authority); ok {
		return true
	}
	if err != nil {
		log.Errorf("user id [%d] privilege [%v] error:%s", ctx.UserId(), authority, err.Error())
	}
	return
}

func (m *PlatformCore) CheckUserNameExist(ctx *itypes.Context, strUserName string) (code itypes.BizCode) {
	if ok, err := m.userDAO.CheckActiveUserByUserName(strUserName); err != nil {
		log.Errorf("query by user name %s error [%s]", strUserName, err.Error())
		return itypes.NewBizCodeDatabaseError()
	} else if ok {
		return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
	}
	return itypes.BizOK
}

func (m *PlatformCore) CheckUserEmailExist(ctx *itypes.Context, strEmail string) (code itypes.BizCode) {

	if ok, err := m.userDAO.CheckActiveUserByEmail(strEmail); err != nil {
		log.Errorf("query by email error [%s]", err.Error())
		return itypes.NewBizCodeDatabaseError()
	} else if ok {
		return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST, "email already exists")
	}
	return itypes.BizOK
}

func (m *PlatformCore) CheckUserPhoneExist(ctx *itypes.Context, strPhone string) (code itypes.BizCode) {

	if ok, err := m.userDAO.CheckActiveUserByPhone(strPhone); err != nil {
		log.Error(err.Error())
		return itypes.NewBizCodeDatabaseError()
	} else if ok {
		return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
	}
	return itypes.BizOK
}

func (m *PlatformCore) ListUser(ctx *itypes.Context, req *proto.PlatformListUserReq) (totalUsers []*proto.PlatformTotalUser, total int64, code itypes.BizCode) {
	var err error
	var users = make([]*proto.PlatformUser, 0)
	totalUsers = make([]*proto.PlatformTotalUser, 0)
	if users, total, err = m.userRoleDAO.SelectUsers(req); err != nil {
		log.Errorf(err.Error())
		return nil, 0, itypes.NewBizCodeDatabaseError()
	}
	for _, user := range users {
		sysUser := &proto.PlatformTotalUser{
			UserId:      user.UserId,
			UserName:    user.UserName,
			UserAlias:   user.UserAlias,
			PhoneNumber: user.PhoneNumber,
			Email:       user.Email,
			Remark:      user.UserRemark,
			LoginTime:   user.LoginTime,
			RoleName:    user.RoleName,
			CreateUser:  user.CreateUser,
			State:       user.State,
			CreatedTime: user.CreatedTime,
		}
		totalUsers = append(totalUsers, sysUser)
	}
	return
}

func (m *PlatformCore) CreateUser(ctx *itypes.Context, req *proto.PlatformCreateUserReq) (do *models.UserDO, code itypes.BizCode) {

	var err error
	var role *models.RoleDO

	if req.PhoneNumber != "" && !utils.VerifyMobileFormat(req.PhoneNumber) {
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, "invalid phone number")
	}

	if req.RoleName != "" {
		if role, err = m.roleDAO.SelectRoleByName(req.RoleName); err != nil {
			log.Errorf(err.Error())
			return nil, itypes.NewBizCodeDatabaseError()
		}
		if role == nil || role.GetId() == 0 {
			err = log.Errorf("role name [%s] not found", req.RoleName)
			return nil, itypes.NewBizCode(itypes.CODE_NOT_FOUND, err.Error())
		}

		//TODO: merge two operations into one transaction later

		// 添加用户角色
		m.cas.AddUserRole(req.UserName, req.RoleName)
	} else {
		err = log.Errorf("no select role name")
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	var lastId int64
	var strSalt = utils.GenerateSalt()
	do = &models.UserDO{
		UserName:    req.UserName,
		Password:    req.Password,
		Salt:        strSalt,
		UserAlias:   req.UserAlias,
		IsAdmin:     false,
		PhoneNumber: req.PhoneNumber,
		Email:       req.Email,
		Remark:      req.Remark,
		CreateUser:  ctx.UserName(),
		EditUser:    ctx.UserName(),
		LoginTime:   utils.Now64(),
		Deleted:     false,
		State:       dao.UserState_Enabled,
	}
	if lastId, err = m.userDAO.Insert(do); err != nil {
		log.Errorf(err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	do.SetId(int32(lastId))
	if err = m.userRoleDAO.Insert(&models.UserRoleDO{
		UserName:   req.UserName,
		RoleName:   req.RoleName,
		CreateUser: ctx.UserName(),
		EditUser:   ctx.UserName(),
		Deleted:    false,
	}); err != nil {
		log.Errorf(err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	return do, itypes.BizOK
}

// 修改账户（系统用户）信息，包括修改其对应的角色
func (m *PlatformCore) EditUser(ctx *itypes.Context, req *proto.PlatformEditUserReq) (code itypes.BizCode) {
	var err error
	var user *models.UserDO
	var userRoleDo = make([]*models.UserRoleDO, 0)
	if user, code = m.GetUserByName(ctx, req.UserName); !code.Ok() {
		log.Errorf("edit user return code [%v]", code)
		return code
	}

	if req.PhoneNumber != "" && user.PhoneNumber != req.PhoneNumber {
		if !utils.VerifyMobileFormat(req.PhoneNumber) {
			return itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, "invalid phone number")
		}
	}

	if user.Email != req.Email {
		if !utils.VerifyEmailFormat(req.Email) {
			return itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, "invalid email")
		}
		var ok bool
		if ok, err = m.userDAO.CheckActiveUserByEmail(req.Email); err != nil {
			log.Errorf(err.Error())
			return itypes.NewBizCodeDatabaseError()
		} else if ok {
			return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
		}
	}

	if userRoleDo, err = m.userRoleDAO.SelectUserByName(req.UserName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}

	user.PhoneNumber = req.PhoneNumber
	user.Remark = req.Remark
	user.UserAlias = req.UserAlias
	// user.Password = req.Password
	user.Email = req.Email
	user.EditUser = ctx.UserName()
	if err = m.userDAO.UpdateByName(user,
		models.USER_COLUMN_USER_ALIAS,
		models.USER_COLUMN_REMARK,
		// models.USER_COLUMN_PASSWORD,
		models.USER_COLUMN_EMAIL,
		models.USER_COLUMN_PHONE_NUMBER,
		models.USER_COLUMN_EDIT_USER); err != nil {

		log.Errorf("%s", err)
		return itypes.NewBizCodeDatabaseError()
	}

	if strings.TrimSpace(req.RoleName) == "" {
		return itypes.BizOK
	}

	if req.UserName == ctx.UserName() {
		err = log.Errorf("can't edit own role")
		return itypes.NewBizCode(itypes.CODE_ACCESS_DENY, err.Error())
	}
	if user.IsAdmin {
		err = log.Errorf("user %s is supper administrator", user.UserName)
		return itypes.NewBizCode(itypes.CODE_ACCESS_DENY, err.Error())
	}

	role, err := m.roleDAO.SelectRoleByName(req.RoleName)
	if err != nil {
		err = log.Errorf("select role by name error [%s]", err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if role == nil || role.GetId() == 0 {
		err = log.Errorf("role name [%s] not found", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_NOT_FOUND, err.Error())
	}

	// 用户角色更新
	for _, userRole := range userRoleDo {
		m.cas.DeleteUserRole(userRole.UserName, userRole.RoleName)
		m.cas.AddUserRole(userRole.UserName, req.RoleName)
	}
	//upsert a new role of platform
	if err = m.userRoleDAO.Upsert(&models.UserRoleDO{
		UserName:   user.GetUserName(),
		RoleName:   req.RoleName,
		Deleted:    false,
		CreateUser: ctx.UserName(),
		EditUser:   ctx.UserName(),
	}, models.USER_ROLE_COLUMN_ROLE_NAME, models.USER_ROLE_COLUMN_EDIT_USER); err != nil {

		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) ResetUserPassword(ctx *itypes.Context, req *proto.PlatformResetPasswordReq) (code itypes.BizCode) {
	var err error
	var user *models.UserDO

	if user, code = m.GetUserByName(ctx, req.UserName); !code.Ok() {
		log.Errorf("reset user password return code [%v]", code)
		return code
	}
	user.EditUser = ctx.UserName()
	user.Password = req.NewPassword
	if err = m.userDAO.UpdateByName(user, models.USER_COLUMN_PASSWORD, models.USER_COLUMN_EDIT_USER); err != nil {
		log.Error(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) EnableUser(ctx *itypes.Context, req *proto.PlatformEnableUserReq) (r *proto.PlatformEnableUserResp, code itypes.BizCode) {
	if err := m.userDAO.UpdateUserState(req.UserName, dao.UserState_Enabled); err != nil {
		log.Errorf(err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	return &proto.PlatformEnableUserResp{}, itypes.BizOK
}

func (m *PlatformCore) DisableUser(ctx *itypes.Context, req *proto.PlatformDisableUserReq) (r *proto.PlatformDisableUserResp, code itypes.BizCode) {
	if err := m.userDAO.UpdateUserState(req.UserName, dao.UserState_Disabled); err != nil {
		log.Errorf(err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	return &proto.PlatformDisableUserResp{}, itypes.BizOK
}

func (m *PlatformCore) DeleteUser(ctx *itypes.Context, req *proto.PlatformDeleteUserReq) (code itypes.BizCode) {
	var err error
	var user *models.UserDO
	if req.UserName == "" {
		err = log.Errorf("user name to delete is nil")
		return itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
	}

	if user, code = m.GetUserByName(ctx, req.UserName); !code.Ok() {
		log.Errorf("delete user return code [%v] ", code)
		return code
	}

	if user.GetId() == 0 {
		err = log.Errorf("user %s not found", req.UserName)
		return itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
	}

	if user.IsAdmin {
		err = log.Errorf("user %s is supper administrator", req.UserName)
		return itypes.NewBizCode(itypes.CODE_ACCESS_DENY, err.Error())
	}

	if err = m.userRoleDAO.Delete(&models.UserRoleDO{
		UserName: req.UserName,
		EditUser: ctx.UserName(),
		Deleted:  true,
	}); err != nil {
		log.Errorf("delete role by user name error [%s]", err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	m.cas.DeleteUser(req.UserName)
	user.Deleted = true
	user.EditUser = ctx.UserName()
	if err = m.userDAO.DeleteUser(user); err != nil {
		log.Errorf("delete user error [%s]", err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) DeleteUsers(ctx *itypes.Context, req *proto.PlatformDeleteUsersReq) (code itypes.BizCode) {
	var err error
	for _, name := range req.UserNames {
		if name == "" {
			err = log.Errorf("user name to delete is nil")
			return itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
		}
	}

	dos, err := m.userDAO.SelectUsersByNames(req.UserNames)
	if err != nil {
		err = log.Errorf("get users error [%v] ", err.Error())
		return itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	if len(dos) == 0 || len(dos) != len(req.UserNames) {
		err = log.Errorf("user count not match with request")
		return itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}
	for _, do := range dos {
		m.DeleteUser(ctx, &proto.PlatformDeleteUserReq{UserName: do.UserName})
	}
	return itypes.BizOK
}

func (m *PlatformCore) GetUserByName(ctx *itypes.Context, strUserName string) (do *models.UserDO, code itypes.BizCode) {

	var err error
	do, err = m.userDAO.SelectUserByName(strUserName)
	if err != nil {
		log.Errorf(err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}

	if do == nil || do.GetId() == 0 {
		log.Errorf("user [%s] not found", strUserName)
		return nil, itypes.NewBizCode(itypes.CODE_NOT_FOUND)
	}
	return do, itypes.BizOK
}

func (m *PlatformCore) ListRole(ctx *itypes.Context, req *proto.PlatformListRoleReq) (roleLists []*proto.PlatformSysRole, total int64, code itypes.BizCode) {
	var err error
	log.Debugf("request processing...")
	var roles = make([]*proto.PlatformRole, 0)
	roleLists = make([]*proto.PlatformSysRole, 0)
	if roles, total, err = m.roleDAO.SelectPlatformRoles(req.PageNo, req.PageSize, req.RoleName); err != nil {
		log.Errorf(err.Error())
		return
	}
	for _, role := range roles {
		privileges := m.cas.GetRoleAuthority(role.RoleName)
		roleLists = append(roleLists, &proto.PlatformSysRole{
			Id:          role.Id,
			RoleName:    role.RoleName,
			RoleAlias:   role.RoleAlias,
			CreateUser:  role.CreateUser,
			IsInherent:  role.IsInherent,
			Remark:      role.Remark,
			CreatedTime: role.CreatedTime,
			Privileges:  privileges,
		})
	}
	return
}

func (m *PlatformCore) CheckUserPassword(ctx *itypes.Context, strUserName, strPassword string) (ok bool, code itypes.BizCode) {
	var do *models.UserDO

	if do, code = m.GetUserByName(ctx, strUserName); !code.Ok() {
		log.Errorf("check user password return code [%v]", code)
		return
	}
	if strPassword != do.Password {
		return false, itypes.BizOK
	}
	return true, itypes.BizOK
}

func (m *PlatformCore) CreateRole(ctx *itypes.Context, req *proto.PlatformCreateRoleReq) (code itypes.BizCode) {
	var err error
	var exist bool

	if exist, err = m.roleDAO.CheckRoleExistByName(req.RoleName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}

	if exist {
		log.Errorf("role name [%s] already exist", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
	}

	if _, err = m.roleDAO.Insert(&models.RoleDO{
		RoleName:   req.RoleName,
		CreateUser: ctx.UserName(),
		EditUser:   ctx.UserName(),
		Remark:     req.Remark,
	}); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) EditRole(ctx *itypes.Context, req *proto.PlatformEditRoleReq) (code itypes.BizCode) {
	var err error
	var ok bool
	var do *models.RoleDO
	// 查询role表获取信息
	if do, err = m.roleDAO.SelectRoleById(req.Id); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if do == nil || do.GetId() == 0 {
		err = log.Errorf("role is not found")
		return itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}
	// 若角色名称相同则直接更新描述
	if do.RoleName == req.RoleName {
		do.Remark = req.Remark
		if err = m.roleDAO.Update(do,
			models.ROLE_COLUMN_ROLE_NAME,
			models.ROLE_COLUMN_REMARK); err != nil {
			log.Errorf(err.Error())
			return itypes.NewBizCodeDatabaseError()
		}
		return itypes.BizOK
	}
	// 检查名称是否存在
	if ok, err = m.roleDAO.CheckRoleExistByName(req.RoleName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if ok {
		log.Errorf("role name [%s] already exist", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST, "role name already exist")
	}
	//角色权限继承 roleA 继承/获取 roleB权限
	m.cas.InheritRoleAuthority(req.RoleName, do.RoleName)
	// 角色更名时，用户继承新角色,并删除旧角色
	m.cas.InheritUserRole(req.RoleName, do.RoleName)
	m.cas.DeleteRole(do.RoleName)
	// 更新用户角色表
	var dos = make([]*models.UserRoleDO, 0)
	if dos, err = m.userRoleDAO.SelectUserByRole(do.RoleName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	for _, doRole := range dos {
		doRole.RoleName = req.RoleName
		err = m.userRoleDAO.UpdateUserById(doRole)
		if err != nil {
			log.Errorf(err.Error())
			continue
		}
	}

	do.RoleName = req.RoleName
	do.Remark = req.Remark
	// 更新role表
	if err = m.roleDAO.Update(do,
		models.ROLE_COLUMN_ROLE_NAME,
		models.ROLE_COLUMN_REMARK,
	); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) DeleteRole(ctx *itypes.Context, req *proto.PlatformDeleteRoleReq) (code itypes.BizCode) {
	var exist bool
	var err error
	// 不可以删除超级管理员角色
	if req.RoleName == InherentAdminRole {
		err = log.Errorf("role name [%s] is supper administrator role", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_ACCESS_DENY, err.Error())
	}

	if exist, err = m.roleDAO.CheckRoleExistByName(req.RoleName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if !exist {
		log.Warnf("role name [%s] is not found", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_NOT_FOUND, "role name is not found")
	}

	var dos = make([]*models.UserRoleDO, 0)
	if dos, err = m.userRoleDAO.SelectUserByRole(req.RoleName); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if len(dos) > 0 {
		log.Warnf("role: %s had %d user", req.RoleName, len(dos))
		return itypes.NewBizCode(itypes.CODE_ACCESS_VIOLATE, "role has too many users to delete")
	}
	// 删除角色
	m.cas.DeleteRole(req.RoleName)

	if err = m.roleDAO.Delete(&models.RoleDO{
		RoleName: req.RoleName,
		Deleted:  true,
		EditUser: ctx.UserName(),
	}); err != nil {
		log.Errorf(err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	return itypes.BizOK
}

func (m *PlatformCore) AuthRole(ctx *itypes.Context, req *proto.PlatformAuthRoleReq) (code itypes.BizCode) {
	var err error
	// 不可以改超级管理员角色的权限，超级管理员角色必须拥有所有权限
	if req.RoleName == InherentAdminRole {
		err = log.Errorf("role name [%s] is supper administrator role", req.RoleName)
		return itypes.NewBizCode(itypes.CODE_ACCESS_DENY, err.Error())
	}
	role, err := m.roleDAO.SelectRoleByName(req.RoleName)
	if err != nil {
		err = log.Errorf("get role error [%s]", err.Error())
		return itypes.NewBizCodeDatabaseError()
	}
	if role == nil || role.GetId() == 0 {
		err = log.Errorf("role is not found")
		return itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	if len(req.Privilege) == 0 {
		m.cas.DeleteRole(req.RoleName)
		return itypes.BizOK
	}
	// 获取具有角色的用户
	res, err := m.cas.GetUsersForRole(req.RoleName)
	if err != nil {
		log.Errorf("get users for role:%s error:%s", req.RoleName, err.Error())
	}
	m.cas.DeleteRole(req.RoleName)
	for _, authority := range req.Privilege {
		role := req.RoleName
		path := m.cas.GetPrivilegePath(authority)
		if strings.EqualFold(path, "") {
			log.Errorf("unknown authority : %s", authority)
			continue
		}
		// 给角色授予权限
		m.cas.AddRoleAuthority(role, path, authority)
		if authority == privilege.UserAdd || authority == privilege.UserEdit {
			strRolePath := m.cas.GetPrivilegePath(privilege.RoleAccess)
			m.cas.AddRoleAuthority(role, strRolePath, privilege.RoleAccess) //角色拥有用户添加和编辑权限默认附带角色查看权限
		}
	}
	for _, user := range res {
		// 给用户添加角色
		m.cas.AddUserRole(user, req.RoleName)
	}
	return itypes.BizOK
}

// 获取 账户或者角色 所拥有的权限
func (m *PlatformCore) InquireAuth(ctx *itypes.Context, req *proto.PlatformInquireAuthReq) (auth []string, code itypes.BizCode) {
	auth = make([]string, 0)
	switch req.NameType {
	case proto.TypeUser:
		auth = m.cas.GetUserRoleList(req.Name)
	case proto.TypeRole:
		auth = m.cas.GetRoleAuthority(req.Name)
	default:
		return auth, itypes.NewBizCode(itypes.CODE_TYPE_UNDEFINED, "auth type not defined")
	}
	return auth, itypes.BizOK
}

func (m *PlatformCore) UserQuery(ctx *itypes.Context, req *proto.PlatformUserQueryReq) (userList proto.PlatformUserQueryResp, total int64, code itypes.BizCode) {
	var err error
	var userName = make([]*models.UserDO, 0)
	userList = proto.PlatformUserQueryResp{
		NameList: make([]string, 0),
	}
	if userName, total, err = m.userDAO.SelectUserName(req); err != nil {
		log.Errorf("select user name list error:%s\n", err.Error())
		return
	}
	for _, name := range userName {
		userList.NameList = append(userList.NameList, name.UserName)
	}
	return
}

func (m *PlatformCore) PrivilegeTree(ctx *itypes.Context, req *proto.PlatformPrivilegeTreeReq) (resp *proto.PlatformPrivilegeTreeResp, code itypes.BizCode) {
	return &proto.PlatformPrivilegeTreeResp{
		TreeList: m.cas.GetAllPrivileges(),
	}, itypes.BizOK
}

func (m *PlatformCore) GetEmailConfig(ctx *itypes.Context, req *proto.PlatformGetEmailConfigReq) (cfg *email.EmailConfig, code itypes.BizCode) {
	var err error
	cfg, err = m.dictionaryDAO.SelectEmailConfig()
	if err != nil {
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
	}
	return cfg, itypes.BizOK
}

func (m *PlatformCore) SetEmailConfig(ctx *itypes.Context, req *proto.PlatformSetEmailConfigReq) (resp *proto.PlatformSetEmailConfigResp, code itypes.BizCode) {
	if !utils.VerifyEmailFormat(req.SmtpName) {
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, "malformed email address")
	}
	var err error
	emailServerDo := &models.DictionaryDO{
		Name:        dao.Dictionary_Name_Email_Server,
		ConfigKey:   dao.Dictionary_Key_Email_Server,
		ConfigValue: req.SmtpServer,
		Remark:      dao.Dictionary_Remark_Email_Server,
		Deleted:     false,
	}
	err = m.dictionaryDAO.Upsert(emailServerDo)
	if err != nil {
		log.Errorf("emailServerDo error:%s", err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	portDo := &models.DictionaryDO{
		Name:        dao.Dictionary_Name_Email_Port,
		ConfigKey:   dao.Dictionary_Key_Email_Port,
		ConfigValue: req.SmtpPort,
		Remark:      dao.Dictionary_Remark_Email_Port,
		Deleted:     false,
	}
	err = m.dictionaryDAO.Upsert(portDo)
	if err != nil {
		log.Errorf("portDo error:%s", err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	emailNameDo := &models.DictionaryDO{
		Name:        dao.Dictionary_Name_Email_Name,
		ConfigKey:   dao.Dictionary_Key_Email_Name,
		ConfigValue: req.SmtpName,
		Remark:      dao.Dictionary_Remark_Email_Name,
		Deleted:     false,
	}
	err = m.dictionaryDAO.Upsert(emailNameDo)
	if err != nil {
		log.Errorf("emailServerDo error:%s", err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	autoCodeDo := &models.DictionaryDO{
		Name:        dao.Dictionary_Name_Email_Auth_Code,
		ConfigKey:   dao.Dictionary_Key_Email_Auth_Code,
		ConfigValue: req.AuthCode,
		Remark:      dao.Dictionary_Remark_Email_Auth_Code,
		Deleted:     false,
	}
	err = m.dictionaryDAO.Upsert(autoCodeDo)
	if err != nil {
		log.Errorf("autoCodeDo error:%s\n", err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	SendNameDo := &models.DictionaryDO{
		Name:        dao.Dictionary_Name_Email_Send_Name,
		ConfigKey:   dao.Dictionary_Key_Email_Send_Name,
		ConfigValue: req.SendName,
		Remark:      dao.Dictionary_Remark_Email_Send_Name,
		Deleted:     false,
	}
	err = m.dictionaryDAO.Upsert(SendNameDo)
	if err != nil {
		log.Errorf("emailServerDo error:%s\n", err.Error())
		return nil, itypes.NewBizCodeDatabaseError()
	}
	return &proto.PlatformSetEmailConfigResp{}, itypes.BizOK
}

func (m *PlatformCore) CheckExist(ctx *itypes.Context, req *proto.PlatformCheckExistReq) (code itypes.BizCode) {
	switch req.CheckType {
	case itypes.CheckType_UserName:
		{
			if do, err := m.userDAO.SelectUserByName(req.Name); err != nil {
				log.Errorf(err.Error())
				return itypes.NewBizCodeDatabaseError()
			} else if do != nil && do.GetId() != 0 {
				return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
			}
		}
	case itypes.CheckType_UserPhoneNumber:
		{
			if do, err := m.userDAO.SelectUserByPhone(req.Name); err != nil {
				log.Errorf(err.Error())
				return itypes.NewBizCodeDatabaseError()
			} else if do != nil && do.GetId() != 0 {
				return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
			}
		}
	case itypes.CheckType_UserEmail:
		{
			if do, err := m.userDAO.SelectUserByEmail(req.Name); err != nil {
				log.Errorf(err.Error())
				return itypes.NewBizCodeDatabaseError()
			} else if do != nil && do.GetId() != 0 {
				return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
			}
		}
	case itypes.CheckType_RoleName:
		{
			if do, err := m.roleDAO.SelectRoleByName(req.Name); err != nil {
				log.Errorf(err.Error())
				return itypes.NewBizCodeDatabaseError()
			} else if do != nil && do.GetId() != 0 {
				return itypes.NewBizCode(itypes.CODE_ALREADY_EXIST)
			}
		}
	}
	return itypes.NewBizCode(itypes.CODE_NOT_FOUND)
}

func (m *PlatformCore) ListRoleUser(ctx *itypes.Context, req *proto.PlatformListRoleUserReq) (users []*proto.PlatformUser, total int64, code itypes.BizCode) {
	var err error

	if users, total, err = m.userRoleDAO.SelectRoleUsers(req.RoleName, req.PageNo, req.PageSize); err != nil {
		log.Errorf(err.Error())
		return nil, 0, itypes.NewBizCodeDatabaseError()
	}
	return
}

func (m *PlatformCore) ListOperLog(ctx *itypes.Context, req *proto.PlatformListOperLogReq) (list []*proto.OperLog, total int64, code itypes.BizCode) {
	var err error
	_ = err
	return
}

func (m *PlatformCore) UploadFile(c *gin.Context) (resp *proto.UploadFileResp, code itypes.BizCode) {

	var err error
	fileHeader, err := c.FormFile(proto.UploadFileData)
	if err != nil {
		err = log.Errorf("form file key [%s] read error [%s]", proto.UploadFileData, err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
	}
	log.Infof("file data name:[%s],size:[%d]", fileHeader.Filename, fileHeader.Size)

	strFileTime := fmt.Sprintf("%v", time.Now().UnixNano())
	strFileName, ok := c.GetPostForm(proto.UploadFileName)
	if !ok {
		log.Infof("form key [%s] not found", proto.UploadFileName)
		// err := log.Errorf("form key [%s] not found", proto.UploadFileName)
		// return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
		strFileName = strFileTime + strings.Replace(strings.TrimSpace(fileHeader.Filename), " ", "", -1)
	} else {
		strFileName = strFileTime + strings.Replace(strings.TrimSpace(strFileName), " ", "", -1)
	}

	var dir string
	if strings.HasSuffix(m.cfg.ImagePath, "/") {
		dir = m.cfg.ImagePath
	} else {
		dir = m.cfg.ImagePath + "/"
	}

	if err = c.SaveUploadedFile(fileHeader, dir+strFileName); err != nil {
		err = log.Errorf("save file error [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	strFileUrl := m.cfg.ImagePrefix + "/" + strFileName
	return &proto.UploadFileResp{
		FileName: strFileName,
		FileUrl:  strFileUrl,
	}, itypes.BizOK
}

func (m *PlatformCore) UploadFileByBase64(c *gin.Context, req *proto.UploadFileReq) (resp *proto.UploadFileResp, code itypes.BizCode) {
	if strings.TrimSpace(req.FileName) == "" {
		err := log.Errorf("form key [%s] not found", proto.UploadFileName)
		return nil, itypes.NewBizCode(itypes.CODE_INVALID_PARAMS, err.Error())
	}

	if strings.Contains(req.FileData, ";base64,") {
		s := strings.Split(req.FileData, ",")
		req.FileData = s[1]
	}

	log.Truncate(log.LEVEL_INFO, 1000, "after remove suffix: [%s]", req.FileData)
	extension := path.Ext(req.FileName)
	extension = strings.ToLower(extension)
	switch extension {
	case ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp":
	// case ".png":

	default:
		arr := strings.Split(req.FileName, ".")
		req.FileName = arr[len(arr)-1] + ".png"
	}

	// req.FileData = strings.Replace(req.FileData, "data:image/png;base64,", "", 1)

	strFileTime := fmt.Sprintf("%v", time.Now().UnixNano())
	strFileName := strFileTime + strings.Replace(strings.TrimSpace(req.FileName), " ", "", -1)

	// 解码 Base64 数据
	decodedData, err := base64.StdEncoding.DecodeString(req.FileData)
	if err != nil {
		err = log.Errorf("Failed to decode Base64 data:[%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	// 创建一个图片对象
	img, _, err := image.Decode(strings.NewReader(string(decodedData)))
	if err != nil {
		err = log.Errorf("create image error: [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	var dir string
	if strings.HasSuffix(m.cfg.ImagePath, "/") {
		dir = m.cfg.ImagePath
	} else {
		dir = m.cfg.ImagePath + "/"
	}
	log.Infof("strFileName:[%s]", dir+strFileName)

	// 将图片保存到文件
	dst := dir + strFileName

	err = os.MkdirAll(filepath.Dir(dst), 0750)
	if err != nil {
		err = log.Errorf("create directory error: [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	// out, err := os.Create(dst)
	out, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, os.FileMode(0644))
	if err != nil {
		err = log.Errorf("create file error: [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	defer out.Close()

	// 以 PNG 格式保存图片
	err = png.Encode(out, img)
	if err != nil {
		err = log.Errorf("save file error: [%s]", err.Error())
		return nil, itypes.NewBizCode(itypes.CODE_ERROR, err.Error())
	}

	strFileUrl := m.cfg.ImagePrefix + "/" + strFileName
	return &proto.UploadFileResp{
		FileName: strFileName,
		FileUrl:  strFileUrl,
	}, itypes.BizOK
}

func DecodePng(base64Png string) (*bytes.Buffer, error) {
	out := bytes.NewBuffer([]byte{})
	unBased, err := base64.StdEncoding.DecodeString(base64Png)
	if err != nil {
		return nil, err
	}
	im, err := png.Decode(bytes.NewReader(unBased))
	if err != nil {
		return nil, err
	}
	if err = png.Encode(out, im); err != nil {
		return nil, err
	}
	return out, nil
}
