package usecase

import (
	"sort"

	"gateway/internal/model/permission/domain/entity"
	"gateway/internal/model/permission/domain/enum"
	dom "gateway/internal/model/permission/domain/usecase"
)

// buildPermissionTree converts the flat permission list into a parent →
// children tree. Roots (parent == "") are returned in alphabetical order
// by Name for stable client rendering.
func buildPermissionTree(perms []*entity.Permission) []*dom.PermissionTreeNode {
	byParent := make(map[string][]*dom.PermissionTreeNode)
	indexByID := make(map[string]*dom.PermissionTreeNode, len(perms))
	for _, perm := range perms {
		node := permissionToNode(perm)
		indexByID[node.ID] = node
		byParent[perm.Parent] = append(byParent[perm.Parent], node)
	}
	for _, children := range byParent {
		sort.SliceStable(children, func(i, j int) bool {
			return children[i].Name < children[j].Name
		})
	}
	for parentID, children := range byParent {
		if parent, ok := indexByID[parentID]; ok {
			parent.Children = children
		}
	}
	return byParent[""]
}

// filterOpenNodes prunes status=close subtrees (and any parent that has
// no remaining children). Mirrors permission-server's filterOpenNodes.
func filterOpenNodes(nodes []*dom.PermissionTreeNode) []*dom.PermissionTreeNode {
	out := make([]*dom.PermissionTreeNode, 0, len(nodes))
	for _, node := range nodes {
		if node.Status != enum.StatusOpen {
			continue
		}
		if len(node.Children) > 0 {
			node.Children = filterOpenNodes(node.Children)
		}
		out = append(out, node)
	}
	return out
}

// filterByType drops subtrees whose type does not match. Useful for the
// "frontend menu only" client query.
func filterByType(nodes []*dom.PermissionTreeNode, t enum.PermissionType) []*dom.PermissionTreeNode {
	out := make([]*dom.PermissionTreeNode, 0, len(nodes))
	for _, node := range nodes {
		// Category nodes inherit the type of their children when they
		// have no leaf type of their own; filter recursively first.
		var kids []*dom.PermissionTreeNode
		if len(node.Children) > 0 {
			kids = filterByType(node.Children, t)
		}
		if node.Type == t || len(kids) > 0 {
			node.Children = kids
			out = append(out, node)
		}
	}
	return out
}

// getFullParentPermissionIDs walks up the parent chain for each id in
// requestedIDs and returns the deduplicated closure (requested + every
// ancestor). Mirrors permission-server's helper of the same name; used
// when persisting RolePermissions so a tenant prefix-clicking a leaf also
// gets the parent UI sections.
func getFullParentPermissionIDs(
	requestedIDs []string,
	allPermissions []*entity.Permission,
) []string {
	parentByID := make(map[string]string, len(allPermissions))
	for _, perm := range allPermissions {
		parentByID[perm.ID.Hex()] = perm.Parent
	}
	seen := make(map[string]struct{}, len(requestedIDs)*2)
	out := make([]string, 0, len(requestedIDs)*2)
	for _, id := range requestedIDs {
		walkParents(id, parentByID, seen, &out)
	}
	return out
}

func walkParents(
	id string,
	parentByID map[string]string,
	seen map[string]struct{},
	out *[]string,
) {
	for id != "" {
		if _, ok := seen[id]; ok {
			return
		}
		seen[id] = struct{}{}
		*out = append(*out, id)
		parent, ok := parentByID[id]
		if !ok || parent == "" {
			return
		}
		id = parent
	}
}

func permissionToNode(perm *entity.Permission) *dom.PermissionTreeNode {
	return &dom.PermissionTreeNode{
		ID:          perm.ID.Hex(),
		Parent:      perm.Parent,
		Name:        perm.Name,
		HTTPMethods: perm.HTTPMethods,
		HTTPPath:    perm.HTTPPath,
		Status:      perm.Status,
		Type:        perm.Type,
	}
}
