package system

import (
	"context"
	"errors"
	"strconv"

	"kra/internal/conf"
)

// BaseMenu 基础菜单实体
type BaseMenu struct {
	ID             uint
	ParentId       uint
	Path           string
	Name           string
	Hidden         bool
	Component      string
	Sort           int
	ActiveName     string
	KeepAlive      bool
	DefaultMenu    bool
	Title          string
	Icon           string
	CloseTab       bool
	TransitionType string
	Parameters     []*MenuParameter
	MenuBtn        []*MenuBtn
	Children       []*BaseMenu
}

// MenuParameter 菜单参数
type MenuParameter struct {
	ID            uint
	SysBaseMenuID uint
	Type          string
	Key           string
	Value         string
}

// MenuBtn 菜单按钮
type MenuBtn struct {
	ID            uint
	SysBaseMenuID uint
	Name          string
	Desc          string
}

// Menu 菜单（带权限）
type Menu struct {
	BaseMenu
	AuthorityId uint
	MenuId      uint
	Btns        map[string]uint
	Children    []*Menu
}

// AuthorityBtn 角色按钮权限
type AuthorityBtn struct {
	AuthorityId      uint
	SysMenuID        uint
	SysBaseMenuBtnID uint
	BtnName          string
}

// MenuRepo 菜单仓储接口
type MenuRepo interface {
	CreateBaseMenu(ctx context.Context, menu *BaseMenu) error
	UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error
	DeleteBaseMenu(ctx context.Context, id uint) error
	FindBaseMenuByID(ctx context.Context, id uint) (*BaseMenu, error)
	FindBaseMenuByName(ctx context.Context, name string) (*BaseMenu, error)
	FindAllBaseMenus(ctx context.Context) ([]*BaseMenu, error)
	FindBaseMenusByIds(ctx context.Context, ids []string) ([]*BaseMenu, error)
	FindAuthorityMenuIds(ctx context.Context, authorityId uint) ([]string, error)
	FindAuthorityBtns(ctx context.Context, authorityId uint) ([]*AuthorityBtn, error)
	SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error
	CheckMenuInAuthority(ctx context.Context, authorityId uint, menuName string) (bool, error)
}

// MenuUsecase 菜单用例
type MenuUsecase struct {
	repo          MenuRepo
	authRepo      AuthorityRepo
	useStrictAuth bool
}

// NewMenuUsecase 创建菜单用例
func NewMenuUsecase(repo MenuRepo, authRepo AuthorityRepo, useStrictAuth conf.UseStrictAuth) *MenuUsecase {
	return &MenuUsecase{
		repo:          repo,
		authRepo:      authRepo,
		useStrictAuth: bool(useStrictAuth),
	}
}

// AddBaseMenu 添加基础菜单
func (uc *MenuUsecase) AddBaseMenu(ctx context.Context, menu *BaseMenu) error {
	return uc.repo.CreateBaseMenu(ctx, menu)
}

// UpdateBaseMenu 更新基础菜单
func (uc *MenuUsecase) UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error {
	return uc.repo.UpdateBaseMenu(ctx, menu)
}

// DeleteBaseMenu 删除基础菜单
func (uc *MenuUsecase) DeleteBaseMenu(ctx context.Context, id uint) error {
	return uc.repo.DeleteBaseMenu(ctx, id)
}

// GetBaseMenuById 根据ID获取基础菜单
func (uc *MenuUsecase) GetBaseMenuById(ctx context.Context, id uint) (*BaseMenu, error) {
	return uc.repo.FindBaseMenuByID(ctx, id)
}

// GetMenuTree 获取动态菜单树
func (uc *MenuUsecase) GetMenuTree(ctx context.Context, authorityId uint) ([]*Menu, error) {
	treeMap, err := uc.getMenuTreeMap(ctx, authorityId)
	if err != nil {
		return nil, err
	}
	menus := treeMap[0]
	for i := range menus {
		uc.getChildrenList(menus[i], treeMap)
	}
	return menus, nil
}

func (uc *MenuUsecase) getMenuTreeMap(ctx context.Context, authorityId uint) (map[uint][]*Menu, error) {
	treeMap := make(map[uint][]*Menu)

	// 获取角色的菜单ID
	menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId)
	if err != nil {
		return nil, err
	}

	// 获取基础菜单
	baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds)
	if err != nil {
		return nil, err
	}

	// 获取按钮权限
	btns, err := uc.repo.FindAuthorityBtns(ctx, authorityId)
	if err != nil {
		return nil, err
	}

	// 构建按钮map
	btnMap := make(map[uint]map[string]uint)
	for _, b := range btns {
		if btnMap[b.SysMenuID] == nil {
			btnMap[b.SysMenuID] = make(map[string]uint)
		}
		btnMap[b.SysMenuID][b.BtnName] = authorityId
	}

	// 构建菜单树
	for _, base := range baseMenus {
		menu := &Menu{
			BaseMenu:    *base,
			AuthorityId: authorityId,
			MenuId:      base.ID,
			Btns:        btnMap[base.ID],
		}
		treeMap[base.ParentId] = append(treeMap[base.ParentId], menu)
	}

	return treeMap, nil
}

func (uc *MenuUsecase) getChildrenList(menu *Menu, treeMap map[uint][]*Menu) {
	menu.Children = treeMap[menu.MenuId]
	for i := range menu.Children {
		uc.getChildrenList(menu.Children[i], treeMap)
	}
}

// GetBaseMenuTree 获取基础菜单树
func (uc *MenuUsecase) GetBaseMenuTree(ctx context.Context, authorityID uint) ([]*BaseMenu, error) {
	treeMap, err := uc.getBaseMenuTreeMap(ctx, authorityID)
	if err != nil {
		return nil, err
	}
	menus := treeMap[0]
	for i := range menus {
		uc.getBaseChildrenList(menus[i], treeMap)
	}
	return menus, nil
}

func (uc *MenuUsecase) getBaseMenuTreeMap(ctx context.Context, authorityID uint) (map[uint][]*BaseMenu, error) {
	parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, authorityID)
	if err != nil {
		return nil, err
	}

	var allMenus []*BaseMenu
	treeMap := make(map[uint][]*BaseMenu)

	// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
	if uc.useStrictAuth && parentAuthorityID != 0 {
		menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityID)
		if err != nil {
			return nil, err
		}
		allMenus, err = uc.repo.FindBaseMenusByIds(ctx, menuIds)
		if err != nil {
			return nil, err
		}
	} else {
		allMenus, err = uc.repo.FindAllBaseMenus(ctx)
		if err != nil {
			return nil, err
		}
	}

	for _, m := range allMenus {
		treeMap[m.ParentId] = append(treeMap[m.ParentId], m)
	}
	return treeMap, nil
}

func (uc *MenuUsecase) getBaseChildrenList(menu *BaseMenu, treeMap map[uint][]*BaseMenu) {
	menu.Children = treeMap[menu.ID]
	for i := range menu.Children {
		uc.getBaseChildrenList(menu.Children[i], treeMap)
	}
}

// GetInfoList 获取菜单列表（树形）
func (uc *MenuUsecase) GetInfoList(ctx context.Context, authorityID uint) ([]*BaseMenu, error) {
	return uc.GetBaseMenuTree(ctx, authorityID)
}

// AddMenuAuthority 为角色增加菜单权限
func (uc *MenuUsecase) AddMenuAuthority(ctx context.Context, menus []*BaseMenu, adminAuthorityID, authorityId uint) error {
	// 检查权限
	parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, adminAuthorityID)
	if err != nil {
		return err
	}

	// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
	if uc.useStrictAuth && parentAuthorityID != 0 {
		menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, adminAuthorityID)
		if err != nil {
			return err
		}

		for _, menu := range menus {
			hasMenu := false
			idStr := strconv.Itoa(int(menu.ID))
			for _, mid := range menuIds {
				if idStr == mid {
					hasMenu = true
					break
				}
			}
			if !hasMenu {
				return errors.New("添加失败,请勿跨级操作")
			}
		}
	}

	// 设置菜单权限
	menuIds := make([]uint, len(menus))
	for i, m := range menus {
		menuIds[i] = m.ID
	}
	return uc.repo.SetMenuAuthority(ctx, authorityId, menuIds)
}

// GetMenuAuthority 获取角色的菜单权限
func (uc *MenuUsecase) GetMenuAuthority(ctx context.Context, authorityId uint) ([]*Menu, error) {
	menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId)
	if err != nil {
		return nil, err
	}

	baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds)
	if err != nil {
		return nil, err
	}

	menus := make([]*Menu, len(baseMenus))
	for i, base := range baseMenus {
		menus[i] = &Menu{
			BaseMenu:    *base,
			AuthorityId: authorityId,
			MenuId:      base.ID,
		}
	}
	return menus, nil
}

// UserAuthorityDefaultRouter 用户角色默认路由检查
// 如果用户的默认路由不在其权限菜单中，则设置为404
func (uc *MenuUsecase) UserAuthorityDefaultRouter(ctx context.Context, authorityId uint, defaultRouter string) string {
	if defaultRouter == "" {
		return "404"
	}

	// 检查默认路由是否在用户的权限菜单中
	exists, err := uc.repo.CheckMenuInAuthority(ctx, authorityId, defaultRouter)
	if err != nil || !exists {
		return "404"
	}
	return defaultRouter
}
