package tree

import (
	"bytes"
	"context"
	"errors"

	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
	"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
	netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"go.uber.org/zap"
)

var errNoSuitableNode = errors.New("no node was found to execute the request")

// forEachNode executes callback for each node in the container until true is returned.
// Returns errNoSuitableNode if there was no successful attempt to dial any node.
func (s *Service) forEachNode(ctx context.Context, cntNodes []netmapSDK.NodeInfo, f func(c TreeServiceClient) bool) error {
	for _, n := range cntNodes {
		if bytes.Equal(n.PublicKey(), s.rawPub) {
			return nil
		}
	}

	var called bool
	for _, n := range cntNodes {
		var stop bool
		n.IterateNetworkEndpoints(func(endpoint string) bool {
			ctx, span := tracing.StartSpanFromContext(ctx, "TreeService.IterateNetworkEndpoints",
				trace.WithAttributes(
					attribute.String("endpoint", endpoint),
				))
			defer span.End()

			c, err := s.cache.get(ctx, endpoint)
			if err != nil {
				return false
			}

			s.log.Debug(logs.TreeRedirectingTreeServiceQuery, zap.String("endpoint", endpoint))
			called = true
			stop = f(c)
			return true
		})
		if stop {
			return nil
		}
	}
	if !called {
		return errNoSuitableNode
	}
	return nil
}