package permission

import (
	"fmt"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
	"git.rosy.net.cn/jx-callback/globals/api2"
	"strings"
	"time"

	"git.rosy.net.cn/jx-callback/business/jxutils"

	"git.rosy.net.cn/baseapi/utils"

	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
)

func GetMenu(ctx *jxcontext.Context, userID string) (menus []*model.Menu, err error) {
	if userID == "" {
		return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
	} else {
		if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
			if user.Type&model.UserTypeRole != 0 {
				return dao.GetMenuWithUser(dao.GetDB(), "", 0, 0, userID)
			} else {
				return dao.GetMenu(dao.GetDB(), "", 0, 0, "")
			}
		}
	}
	return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
}

func AddMenu(ctx *jxcontext.Context, menu *model.Menu) (err error) {
	var (
		db = dao.GetDB()
	)
	if menu == nil {
		return fmt.Errorf("添加失败！menu nil")
	}
	if menu.Name == "" || menu.Level == 0 {
		return fmt.Errorf("添加失败！menu 名称和等级必须有值！")
	}
	menus, err := dao.GetMenu(db, menu.Name, menu.Level, 0, "")
	if len(menus) > 0 {
		return fmt.Errorf("添加失败！已存在相同名称的 menu name : %v", menu.Name)
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	dao.WrapAddIDCULDEntity(menu, ctx.GetUserName())
	err = dao.CreateEntity(db, menu)
	dao.Commit(db, txDB)
	return err
}

func UpdateMenu(ctx *jxcontext.Context, menuID int, payload map[string]interface{}, isDelete bool) (num int64, err error) {
	var (
		db = dao.GetDB()
	)
	menu := &model.Menu{}
	menu.ID = menuID
	err = dao.GetEntity(db, menu)
	if err != nil {
		return 0, err
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if !isDelete {
		valid := dao.StrictMakeMapByStructObject(payload, menu, ctx.GetUserName())
		if len(valid) > 0 {
			if num, err = dao.UpdateEntityLogically(db, menu, valid, ctx.GetUserName(), nil); err != nil {
				dao.Rollback(db, txDB)
				return 0, err
			}
		}
	} else {
		menu.DeletedAt = time.Now()
		num, err = dao.UpdateEntity(db, menu, "DeletedAt")
	}
	dao.Commit(db, txDB)
	return num, err
}

func GetRole(ctx *jxcontext.Context, name string) (roles []*model.Role, err error) {
	var (
		db = dao.GetDB()
	)
	roles, err = dao.GetRole(db, name, "")
	task := tasksch.NewParallelTask("GetRole", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			v := batchItemList[0].(*model.Role)
			if v.CityCodes != "" {
				if cityInfos, err := dao.GetPlaces(db, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))); err == nil {
					v.CityInfo = cityInfos
				}
			}
			if v.StoreIDs != "" {
				if stores, err := dao.GetStoreList(db, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ",")), nil, nil, nil, nil, ""); err == nil {
					v.Stores = stores
				}
			}
			return retVal, err
		}, roles)
	tasksch.HandleTask(task, nil, false).Run()
	task.GetResult(0)
	return roles, err
}

func AddRole(ctx *jxcontext.Context, name string) (err error) {
	var (
		db = dao.GetDB()
	)
	roles, err := dao.GetRole(db, "", name)
	if len(roles) > 0 {
		return fmt.Errorf("添加失败！已存在相同名称的 role name : %v", name)
	}
	role := &model.Role{
		Name: name,
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	dao.WrapAddIDCULDEntity(role, ctx.GetUserName())
	err = dao.CreateEntity(db, role)
	dao.Commit(db, txDB)
	return err
}

func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool, brandID int, cityCodes, storeIDs []int) (num int64, err error) {
	var (
		db           = dao.GetDB()
		cityCodesStr []string
		storeIDsStr  []string
	)
	for _, v := range cityCodes {
		cityCodesStr = append(cityCodesStr, utils.Int2Str(v))
	}
	for _, v := range storeIDs {
		storeIDsStr = append(storeIDsStr, utils.Int2Str(v))
	}
	if roleID == 1 {
		return 0, fmt.Errorf("管理员不允许修改！")
	}
	role := &model.Role{}
	role.ID = roleID
	err = dao.GetEntity(db, role)
	if err != nil {
		return 0, err
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if !isDelete {
		role.Name = name
		role.UpdatedAt = time.Now()
		role.LastOperator = ctx.GetUserName()
		role.BrandID = brandID
		role.CityCodes = strings.Join(cityCodesStr, ",")
		role.StoreIDs = strings.Join(storeIDsStr, ",")
		num, err = dao.UpdateEntity(db, role, "Name", "UpdatedAt", "LastOperator", "BrandID", "CityCodes", "StoreIDs")
	} else {
		role.DeletedAt = time.Now()
		num, err = dao.UpdateEntity(db, role, "DeletedAt")
	}
	dao.Commit(db, txDB)
	return num, err
}

func GetUserRole(ctx *jxcontext.Context, userID string) (userRoles []*model.UserRole, err error) {
	return dao.GetUserRole(dao.GetDB(), []string{userID}, nil)
}

func UpdateUserRole(ctx *jxcontext.Context, userIDs []string, roleIDs []int) (err error) {
	var (
		db                = dao.GetDB()
		roleIDMap         = make(map[int]int)
		nowRoleIDMap      = make(map[int]int)
		userRoleMap       = make(map[string][]int)
		addUserRoleMap    = make(map[string][]int)
		deleteUserRoleMap = make(map[string][]int)
	)
	for _, v := range roleIDs {
		roleIDMap[v] = 1
	}
	userRoles, err := dao.GetUserRole(db, userIDs, nil)
	if err != nil {
		return err
	}
	if len(userRoles) > 0 {
		for _, v := range userRoles {
			userRoleMap[v.UserID] = append(userRoleMap[v.UserID], v.RoleID)
		}
	}
	for _, userID := range userIDs {
		nowRoleIDs := userRoleMap[userID]
		for _, nowRoleID := range nowRoleIDs {
			if roleIDMap[nowRoleID] == 0 {
				deleteUserRoleMap[userID] = append(deleteUserRoleMap[userID], nowRoleID)
			}
			nowRoleIDMap[nowRoleID] = 1
		}
		for roleID, _ := range roleIDMap {
			if nowRoleIDMap[roleID] == 0 {
				addUserRoleMap[userID] = append(addUserRoleMap[userID], roleID)
			}
		}
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if len(addUserRoleMap) > 0 {
		for userID, roleIDs := range addUserRoleMap {
			for _, roleID := range roleIDs {
				userRole := &model.UserRole{
					UserID: userID,
					RoleID: roleID,
				}
				dao.WrapAddIDCULDEntity(userRole, ctx.GetUserName())
				err = dao.CreateEntity(db, userRole)
			}
		}
	}
	if len(deleteUserRoleMap) > 0 {
		for userID, roleIDs := range deleteUserRoleMap {
			for _, roleID := range roleIDs {
				userRoles, _ := dao.GetUserRole(db, []string{userID}, []int{roleID})
				if len(userRoles) > 0 {
					userRoles[0].DeletedAt = time.Now()
					userRoles[0].LastOperator = ctx.GetUserName()
					_, err = dao.UpdateEntity(db, userRoles[0], "DeletedAt", "LastOperator")
				}
			}
		}
	}
	for _, v := range userIDs {
		if user, err := dao.GetUserByID(db, "user_id", v); err == nil {
			user.Type = user.Type | model.UserTypeRole
			dao.UpdateEntity(db, user, "Type")
		}
	}
	dao.Commit(db, txDB)
	return err
}

func GetRoleMenu(ctx *jxcontext.Context, roleID int) (roleMenus []*model.RoleMenu, err error) {
	return dao.GetRoleMenu(dao.GetDB(), []int{roleID}, nil)
}

func UpdateRoleMenu(ctx *jxcontext.Context, roleIDs, menuIDs []int) (err error) {
	var (
		db                = dao.GetDB()
		menuIDMap         = make(map[int]int)
		nowMenuIDMap      = make(map[int]int)
		roleMenuMap       = make(map[int][]int)
		addRoleMenuMap    = make(map[int][]int)
		deleteRoleMenuMap = make(map[int][]int)
	)
	for _, v := range menuIDs {
		menuIDMap[v] = 1
	}
	roleMenus, err := dao.GetRoleMenu(db, roleIDs, nil)
	if err != nil {
		return err
	}
	if len(roleMenus) > 0 {
		for _, v := range roleMenus {
			roleMenuMap[v.RoleID] = append(roleMenuMap[v.RoleID], v.MenuID)
		}
	}
	for _, roleID := range roleIDs {
		nowMenuIDs := roleMenuMap[roleID]
		for _, nowMenuID := range nowMenuIDs {
			if menuIDMap[nowMenuID] == 0 {
				deleteRoleMenuMap[roleID] = append(deleteRoleMenuMap[roleID], nowMenuID)
			}
			nowMenuIDMap[nowMenuID] = 1
		}
		for menuID, _ := range menuIDMap {
			if nowMenuIDMap[menuID] == 0 {
				addRoleMenuMap[roleID] = append(addRoleMenuMap[roleID], menuID)
			}
		}
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if len(addRoleMenuMap) > 0 {
		for roleID, menuIDs := range addRoleMenuMap {
			for _, menuID := range menuIDs {
				roleMenu := &model.RoleMenu{
					RoleID: roleID,
					MenuID: menuID,
				}
				dao.WrapAddIDCULDEntity(roleMenu, ctx.GetUserName())
				err = dao.CreateEntity(db, roleMenu)
			}
		}
	}
	if len(deleteRoleMenuMap) > 0 {
		for roleID, menuIDs := range deleteRoleMenuMap {
			for _, menuID := range menuIDs {
				roleMenus, _ := dao.GetRoleMenu(db, []int{roleID}, []int{menuID})
				if len(roleMenus) > 0 {
					roleMenus[0].DeletedAt = time.Now()
					roleMenus[0].LastOperator = ctx.GetUserName()
					_, err = dao.UpdateEntity(db, roleMenus[0], "DeletedAt", "LastOperator")
				}
			}
		}
	}
	dao.Commit(db, txDB)
	return err
}

func GetUserStoresResultMap(userID string) (resultMap map[int]int, err error) {
	var (
		db = dao.GetDB()
	)
	resultMap = make(map[int]int)
	user, _ := dao.GetUserByID(db, "user_id", userID)
	if user != nil && user.BindStoreID != "" {
		for _, v := range strings.Split(user.BindStoreID, ",") {
			resultMap[utils.Str2Int(v)] = utils.Str2Int(v)
		}
		return resultMap, err
	}
	userRoles, err2 := dao.GetUserRole2(db, []string{userID}, nil)
	err = err2
	for _, v := range userRoles {
		var (
			brandIDs, cityCodes, storeIDs []int
		)
		if v.CityCodes == "" && v.StoreIDs == "" {
			continue
		}
		if v.BrandID != 0 {
			brandIDs = append(brandIDs, v.BrandID)
		}
		if v.CityCodes != "0" && v.CityCodes != "" {
			cityCodes = append(cityCodes, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))...)
		}
		if v.StoreIDs != "" {
			storeIDs = append(storeIDs, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ","))...)
		}
		if stores, err := dao.GetStoreList(db, storeIDs, cityCodes, nil, brandIDs, nil, ""); len(stores) > 0 && err == nil {
			for _, v := range stores {
				resultMap[v.ID] = v.ID
			}
		}
	}
	roleList, _ := api2.RoleMan.GetUserRoleList(userID)
	for _, v := range roleList {
		resultMap[v.StoreID] = v.StoreID
	}

	return resultMap, err
}

func IsRoled(ctx *jxcontext.Context) bool {
	if ctx.GetUserName() != "jxadmin" && ctx.GetUserName() != "shifengfix" {
		userRole, _ := dao.GetUserRole(dao.GetDB(), []string{ctx.GetUserID()}, nil)
		for _, v := range userRole {
			if v.RoleID == model.YES {
				return false
			}
		}
		return true
		/*if user, err := dao.GetUserByID(dao.GetDB(), "user_id", ctx.GetUserID()); err == nil {
			if user.Type&model.UserTypeRole != 0 {
				return true
			}
		}*/
	}
	return false
}

func IsRoledByUserID(userID string) bool {
	if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
		if user.Type&model.UserTypeRole != 0 {
			return true
		}
	}
	return false
}
