package service

import (
	"cold-delivery/common/actions"
	dto2 "cold-delivery/common/dto"
	"cold-delivery/common/global"
	cModel "cold-delivery/common/model"
	"encoding/json"
	"fmt"
	"gogs.baozhida.cn/zoie/OAuth-core/service"
	"strings"

	"github.com/pkg/errors"
	"gorm.io/gorm"

	"cold-delivery/app/admin/model"
	"cold-delivery/app/admin/service/dto"
	"gogs.baozhida.cn/zoie/OAuth-core/pkg"
)

type SysMenu struct {
	service.Service
}

// GetPage 获取SysMenu列表
func (e *SysMenu) GetPage(c *dto.SysMenuGetPageReq, menus *[]model.SysMenu) *SysMenu {
	var menu = make([]model.SysMenu, 0)
	err := e.getPage(c, &menu).Error
	if err != nil {
		_ = e.AddError(global.GetFailedErr)
		return e
	}
	for i := 0; i < len(menu); i++ {
		if menu[i].ParentId != 0 {
			continue
		}
		menusInfo := SysMenuCall(&menu, menu[i])
		*menus = append(*menus, menusInfo)
	}

	if len(*menus) == 0 {
		parentMap, flag := getSysMenuParent(&menu)
		if flag {
			for i := 0; i < len(menu); i++ {
				if parentMap[menu[i].ParentId] {
					continue
				}
				menusInfo := SysMenuCall(&menu, menu[i])
				*menus = append(*menus, menusInfo)
			}
		} else {
			for i := 0; i < len(menu); i++ {
				menusInfo := SysMenuCall(&menu, menu[i])
				*menus = append(*menus, menusInfo)
			}
		}
	}

	return e
}

// 获取搜索出来的菜单的父菜单
func getSysMenuParent(menuList *[]model.SysMenu) (map[int]bool, bool) {
	list := *menuList
	var flag = false
	var parentMap = map[int]bool{}
	for j := 0; j < len(list); j++ {
		parentMap[list[j].ParentId] = false
	}

	for j := 0; j < len(list); j++ {
		if _, ok := parentMap[list[j].Id]; !ok {
			continue
		}
		parentMap[list[j].Id] = true
		flag = true
	}
	return parentMap, flag
}

// getPage 菜单分页列表
func (e *SysMenu) getPage(c *dto.SysMenuGetPageReq, list *[]model.SysMenu) *SysMenu {
	var err error
	var data model.SysMenu
	var tempList []model.SysMenu
	err = e.Orm.Model(&data).
		Scopes(dto2.MakeCondition(c.GetNeedSearch())).
		Order("id").
		Find(&tempList).Error
	if err != nil {
		e.Log.Errorf("getSysMenuPage error: %s", err)
		_ = e.AddError(err)
		return e
	}

	// 获取菜单及其子菜单下ID
	for _, menu := range tempList {
		var menuList = make([]model.SysMenu, 0)
		err = e.Orm.Where("menu_path like ?", menu.MenuPath+"%").Order(fmt.Sprintf("sort %s", c.SortOrder)).Order("id").Find(&menuList).Error
		*list = append(*list, menuList...)
	}

	*list = DeduplicateSysMenu(*list)

	return e
}

// Get 获取SysMenu对象
func (e *SysMenu) Get(d *dto.SysMenuGetReq, menuModel *model.SysMenu) *SysMenu {
	err := e.Orm.First(menuModel, d.GetId()).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = global.GetNotFoundOrNoPermissionErr
		} else {
			err = global.GetFailedErr
		}
		_ = e.AddError(err)
		return e
	}
	return e
}

// Insert 创建SysMenu对象
func (e *SysMenu) Insert(c *dto.SysMenuInsertReq) *SysMenu {
	var err error
	var data model.SysMenu

	tx := e.Orm.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	c.Generate(&data)
	err = tx.Create(&data).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		_ = e.AddError(global.CreateFailedErr)
		return e
	}
	c.Id = data.Id

	menuPath, err := e.initPaths(&data)
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		_ = e.AddError(err)
		return e
	}
	err = tx.Model(&data).Where("id = ?", data.Id).Update("menu_path", menuPath).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		_ = e.AddError(global.CreateFailedErr)
		return e
	}

	// 添加菜单后删除角色菜单缓存，防止系统菜单与服务菜单不同步
	err = model.DeleteAllRoleMenuCache()
	if err != nil {
		e.Log.Errorf("删除角色菜单缓存失败 %s", err)
		_ = e.AddError(err)
		return e
	}

	return e
}

func (e *SysMenu) initPaths(menu *model.SysMenu) (menuPath string, err error) {
	var data model.SysMenu
	parentMenu := new(model.SysMenu)
	if menu.ParentId != 0 {
		e.Orm.Model(&data).First(parentMenu, menu.ParentId)
		if parentMenu.MenuPath == "" {
			err = errors.New("父级menu_path异常，请尝试对当前节点父级菜单进行更新操作！")
			return menuPath, err
		}
		menuPath = parentMenu.MenuPath + "/" + pkg.IntToString(menu.Id)
	} else {
		menuPath = "/0/" + pkg.IntToString(menu.Id)
	}
	return menuPath, nil
}

// Update 修改SysMenu对象
func (e *SysMenu) Update(c *dto.SysMenuUpdateReq) *SysMenu {
	var err error
	tx := e.Orm.Debug().Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var menuModel = model.SysMenu{}
	err = tx.First(&menuModel, c.GetId()).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = global.UpdateNotFoundOrNoPermissionErr
		} else {
			err = global.UpdateFailedErr
		}
		_ = e.AddError(err)
		return e
	}
	oldPath := menuModel.MenuPath
	oldParentId := menuModel.ParentId
	c.Generate(&menuModel)

	// 替换旧的path
	if c.ParentId != oldParentId {
		menuPath, err := e.initPaths(&menuModel)
		if err != nil {
			e.Log.Errorf("db error: %s", err)
			_ = e.AddError(err)
			return e
		}
		menuModel.MenuPath = menuPath
	}
	// 更新菜单信息
	err = tx.Model(&menuModel).Save(&menuModel).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		_ = e.AddError(global.UpdateFailedErr)
		return e
	}
	// 替换旧的path
	if oldPath != menuModel.MenuPath {
		var menuList []model.SysMenu
		tx.Where("menu_path like ?", oldPath+"%").Find(&menuList)
		for _, v := range menuList {
			v.MenuPath = strings.Replace(v.MenuPath, oldPath, menuModel.MenuPath, 1)
			err := tx.Model(&v).Update("menu_path", v.MenuPath).Error
			if err != nil {
				e.Log.Errorf("db error: %s", err)
				_ = e.AddError(global.CreateFailedErr)
				return e
			}
		}
	}

	// 修改服务菜单后删除角色菜单缓存，防止系统菜单与服务菜单不同步
	err = model.DeleteAllRoleMenuCache()
	if err != nil {
		_ = e.AddError(err)
		return e
	}

	return e
}

// Remove 删除SysMenu
func (e *SysMenu) Remove(d *dto.SysMenuDeleteReq) *SysMenu {
	var err error

	tx := e.Orm.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	var menuModel model.SysMenu
	err = tx.First(&menuModel, d.GetId()).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = global.DeleteNotFoundOrNoPermissionErr
		} else {
			err = global.DeleteFailedErr
		}
		_ = e.AddError(err)
		return e
	}

	err = e.Orm.Where("menu_path like ?", menuModel.MenuPath+"%").Delete(&model.SysMenu{}).Error
	if err != nil {
		e.Log.Errorf("Delete error: %s", err)
		_ = e.AddError(global.DeleteFailedErr)
		return e
	}

	// 删除服务菜单后删除角色菜单缓存，防止系统菜单与服务菜单不同步
	err = model.DeleteAllRoleMenuCache()
	if err != nil {
		e.Log.Errorf("删除角色菜单缓存失败 %s", err)
		_ = e.AddError(err)
		return e
	}
	return e
}

// GetList 获取菜单数据
func (e *SysMenu) GetList(c *dto.SysMenuGetPageReq, list *[]model.SysMenu) error {
	var err error
	var data model.SysMenu

	err = e.Orm.Model(&data).
		Scopes(
			dto2.MakeCondition(c.GetNeedSearch()),
		).
		Find(list).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.GetFailedErr
	}
	return nil
}

// SysMenuCall 构建菜单树
func SysMenuCall(menuList *[]model.SysMenu, menu model.SysMenu) model.SysMenu {
	list := *menuList

	min := make([]model.SysMenu, 0)
	for j := 0; j < len(list); j++ {

		if menu.Id != list[j].ParentId {
			continue
		}
		mi := model.SysMenu{}
		mi.Id = list[j].Id
		mi.Title = list[j].Title
		mi.Icon = list[j].Icon
		mi.Path = list[j].Path
		mi.MenuType = list[j].MenuType
		mi.Name = list[j].Name
		mi.ParentId = list[j].ParentId
		mi.Component = list[j].Component
		mi.Sort = list[j].Sort
		mi.Visible = list[j].Visible
		mi.Redirect = list[j].Redirect
		mi.CreatedAt = list[j].CreatedAt
		mi.Children = []model.SysMenu{}

		if mi.MenuType != cModel.Button {
			ms := SysMenuCall(menuList, mi)
			min = append(min, ms)
		} else {
			min = append(min, mi)
		}
	}
	menu.Children = min
	return menu
}

// DeduplicateSysMenu 菜单列表去重
func DeduplicateSysMenu(list []model.SysMenu) []model.SysMenu {
	seen := make(map[string]bool)
	deduplicated := []model.SysMenu{}

	for _, v := range list {
		// 将结构体编码为 JSON 字符串
		key, err := json.Marshal(v)
		if err != nil {
			// 处理错误
			continue
		}
		// 将 JSON 字符串作为键插入到 map 中
		if _, ok := seen[string(key)]; !ok {
			seen[string(key)] = true
			deduplicated = append(deduplicated, v)
		}
	}

	return deduplicated
}

// menuLabelCall 递归构造组织数据
func menuLabelCall(eList *[]model.SysMenu, organ dto.MenuLabel) dto.MenuLabel {
	list := *eList

	min := make([]dto.MenuLabel, 0)
	for j := 0; j < len(list); j++ {

		if organ.Id != list[j].ParentId {
			continue
		}
		mi := dto.MenuLabel{}
		mi.Id = list[j].Id
		mi.Label = list[j].Title
		mi.Children = []dto.MenuLabel{}
		if list[j].MenuType != "F" {
			ms := menuLabelCall(eList, mi)
			min = append(min, ms)
		} else {
			min = append(min, mi)
		}
	}
	if len(min) > 0 {
		organ.Children = min
	} else {
		organ.Children = nil
	}
	return organ
}

// SetLabel 修改角色中 设置菜单基础数据
func (e *SysMenu) SetLabel() (m []dto.MenuLabel, err error) {
	var list []model.SysMenu
	err = e.GetList(&dto.SysMenuGetPageReq{}, &list)
	if err != nil {
		return
	}

	m = make([]dto.MenuLabel, 0)
	for i := 0; i < len(list); i++ {
		if list[i].ParentId != 0 {
			continue
		}
		e := dto.MenuLabel{}
		e.Id = list[i].Id
		e.Label = list[i].Title
		menuInfo := menuLabelCall(&list, e)

		m = append(m, menuInfo)
	}
	return
}

// menuCall 构建菜单树
func menuCall(menuList *[]model.SysMenu, menu model.SysMenu) model.SysMenu {
	list := *menuList

	min := make([]model.SysMenu, 0)
	for j := 0; j < len(list); j++ {

		if menu.Id != list[j].ParentId {
			continue
		}
		mi := model.SysMenu{}
		mi.Id = list[j].Id
		mi.Title = list[j].Title
		mi.Icon = list[j].Icon
		mi.Path = list[j].Path
		mi.MenuType = list[j].MenuType
		mi.Name = list[j].Name
		mi.ParentId = list[j].ParentId
		mi.Component = list[j].Component
		mi.Sort = list[j].Sort
		mi.Visible = list[j].Visible
		mi.CreatedAt = list[j].CreatedAt
		mi.Children = []model.SysMenu{}
		ms := menuCall(menuList, mi)
		min = append(min, ms)
	}
	menu.Children = min
	return menu
}

// 通过roleName获取菜单列表
func (e *SysMenu) getByRoleName(roleName string) ([]model.SysMenu, error) {
	var err error

	data := make([]model.SysMenu, 0)

	if roleName == "admin" {
		err = e.Orm.Find(&data).Error
	} else {
		roleMenuIds := make([]int, 0)
		err = e.Orm.Model(&model.SysRoleMenu{}).Select("menu_id").Where("role_key = ? ", roleName).Scan(&roleMenuIds).Error

		if len(roleMenuIds) > 0 {
			if err = recursiveSetMenu(e.Orm, roleMenuIds, &data); err != nil {
				return nil, err
			}
			// 菜单id及父id 去重
			data = menuDistinct(data)
		}
	}

	return data, err
}
func recursiveSetMenu(orm *gorm.DB, mIds []int, menus *[]model.SysMenu) error {
	if len(mIds) == 0 || menus == nil {
		return nil
	}
	var subMenus []model.SysMenu
	err := orm.Where("id in ?", mIds).Find(&subMenus).Error
	if err != nil {
		return err
	}

	subIds := make([]int, 0)
	for _, menu := range subMenus {
		if menu.ParentId != 0 {
			subIds = append(subIds, menu.ParentId)
		}
		//if menu.MenuType != cModel.Button {
		//	*menus = append(*menus, menu)
		//}
		*menus = append(*menus, menu)
	}
	return recursiveSetMenu(orm, subIds, menus)
}

// 去掉重复菜单
func menuDistinct(menuList []model.SysMenu) (result []model.SysMenu) {
	distinctMap := make(map[int]struct{}, len(menuList))
	for _, menu := range menuList {
		if _, ok := distinctMap[menu.Id]; !ok {
			distinctMap[menu.Id] = struct{}{}
			result = append(result, menu)
		}
	}
	return result
}

// SetMenuRole 获取左侧菜单树使用
func (e *SysMenu) SetMenuRole(roleKey string, userId int, p *actions.DataPermission) (m []model.SysMenu, err error) {
	// 从缓存获取角色关联的菜单
	var cacheKey string
	if p.DeptId > 0 {
		//cacheKey = fmt.Sprintf("%s-1-%d", roleKey, userId)
		cacheKey = fmt.Sprintf("%s-1", roleKey)
	} else {
		//cacheKey = fmt.Sprintf("%s-0-%d", roleKey, userId)
		cacheKey = fmt.Sprintf("%s-0", roleKey)
	}
	if err := model.GetRoleMenuCache(cacheKey, &m); err == nil {
		return m, nil
	}
	menus, err := e.getByRoleName(roleKey)
	if err != nil {
		return m, global.GetFailedErr
	}
	m = make([]model.SysMenu, 0)

	var menuList []model.SysMenu

	if p.DeptId == 0 {
		if roleKey == "admin" {
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.visible = 1 and sys_menu.deleted_at is null and is_dept = 0").
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		} else {
			menuIds := make([]int, 0)
			for _, menu := range menus {
				menuIds = append(menuIds, menu.Id)
			}
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.id in (?) and sys_menu.visible = 1 and sys_menu.deleted_at is null and is_dept = 0", menuIds).
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		}
	} else {

		if roleKey == "admin" {
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.visible = 1 and sys_menu.deleted_at is null").
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		} else {
			menuIds := make([]int, 0)
			for _, menu := range menus {
				menuIds = append(menuIds, menu.Id)
			}
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.id in (?) and sys_menu.visible = 1 and sys_menu.deleted_at is null", menuIds).
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		}
	}

	//sort.Sort(model.SysMenuSlice(menuList))
	for i := 0; i < len(menuList); i++ {
		if menuList[i].ParentId != 0 {
			continue
		}
		menusInfo := menuCall(&menuList, menuList[i])
		m = append(m, menusInfo)
	}
	// 保存角色菜单到缓存
	_ = model.SetRoleMenuCache(cacheKey, m)
	return
}
func (e *SysMenu) MenuSelect(roleKey string, userId int, p *actions.DataPermission) (m []model.SysMenu, err error) {

	menus, err := e.getByRoleName(roleKey)
	if err != nil {
		return m, global.GetFailedErr
	}
	m = make([]model.SysMenu, 0)

	var menuList []model.SysMenu

	if p.DeptId == 0 {
		if roleKey == "admin" {
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.visible = 1 and sys_menu.deleted_at is null").
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		} else {
			menuIds := make([]int, 0)
			for _, menu := range menus {
				menuIds = append(menuIds, menu.Id)
			}
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.id in (?) and sys_menu.visible = 1 and sys_menu.deleted_at is null", menuIds).
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		}
	} else {

		if roleKey == "admin" {
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.visible = 1 and sys_menu.deleted_at is null").
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		} else {
			menuIds := make([]int, 0)
			for _, menu := range menus {
				menuIds = append(menuIds, menu.Id)
			}
			err = e.Orm.Table("sys_menu").
				Where("sys_menu.id in (?) and sys_menu.visible = 1 and sys_menu.deleted_at is null", menuIds).
				Order("sys_menu.sort").
				Order("id").
				Scan(&menuList).Error
			if err != nil {
				return m, global.GetFailedErr
			}
		}
	}

	//sort.Sort(model.SysMenuSlice(menuList))
	for i := 0; i < len(menuList); i++ {
		if menuList[i].ParentId != 0 {
			continue
		}
		menusInfo := menuCall(&menuList, menuList[i])
		m = append(m, menusInfo)
	}
	return
}
