package usecase

import (
	"context"

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

// AuthorizationQueryUseCaseParam injects all read-side repos.
type AuthorizationQueryUseCaseParam struct {
	Roles           domrepo.RoleRepository
	Permissions     domrepo.PermissionRepository
	RolePermissions domrepo.RolePermissionRepository
	UserRoles       domrepo.UserRoleRepository
}

type authorizationQueryUseCase struct {
	roles     domrepo.RoleRepository
	perms     domrepo.PermissionRepository
	rolePerms domrepo.RolePermissionRepository
	userRoles domrepo.UserRoleRepository
}

// NewAuthorizationQueryUseCase composes Role + RolePermission +
// Permission to produce the menu/permission map used by GET /me.
func NewAuthorizationQueryUseCase(param AuthorizationQueryUseCaseParam) dom.AuthorizationQueryUseCase {
	return &authorizationQueryUseCase{
		roles:     param.Roles,
		perms:     param.Permissions,
		rolePerms: param.RolePermissions,
		userRoles: param.UserRoles,
	}
}

func (uc *authorizationQueryUseCase) Me(
	ctx context.Context,
	tenantID, uid string,
	includeTree bool,
) (*dom.MePermissionsResponse, error) {
	resp := &dom.MePermissionsResponse{
		UID:         uid,
		TenantID:    tenantID,
		Roles:       []string{},
		Permissions: enum.Permissions{},
	}

	urs, err := uc.userRoles.ListByUser(ctx, tenantID, uid)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	if len(urs) == 0 {
		if includeTree {
			resp.Tree = []*dom.PermissionTreeNode{}
		}
		return resp, nil
	}

	roleIDs := make([]string, 0, len(urs))
	for _, ur := range urs {
		roleIDs = append(roleIDs, ur.RoleID)
	}
	roles, err := uc.roles.ListByTenantAndIDs(ctx, tenantID, roleIDs)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	roleByID := make(map[string]*entity.Role, len(roles))
	for _, role := range roles {
		roleByID[role.ID.Hex()] = role
		if role.Status == enum.StatusOpen {
			resp.Roles = append(resp.Roles, role.Key)
		}
	}

	rps, err := uc.rolePerms.ListByRoles(ctx, tenantID, roleIDs)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	if len(rps) == 0 {
		if includeTree {
			resp.Tree = []*dom.PermissionTreeNode{}
		}
		return resp, nil
	}

	permIDSet := make(map[string]struct{}, len(rps))
	for _, rp := range rps {
		role, ok := roleByID[rp.RoleID]
		if !ok || role.Status != enum.StatusOpen {
			continue
		}
		permIDSet[rp.PermissionID] = struct{}{}
	}
	if len(permIDSet) == 0 {
		if includeTree {
			resp.Tree = []*dom.PermissionTreeNode{}
		}
		return resp, nil
	}
	ids := make([]string, 0, len(permIDSet))
	for id := range permIDSet {
		ids = append(ids, id)
	}
	perms, err := uc.perms.GetByIDs(ctx, ids)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	for _, perm := range perms {
		resp.Permissions[perm.Name] = perm.Status
	}

	if includeTree {
		resp.Tree = filterOpenNodes(buildPermissionTree(perms))
	}
	return resp, nil
}

var _ dom.AuthorizationQueryUseCase = (*authorizationQueryUseCase)(nil)
