package operation

import (
	"context"
	"fmt"
	"time"

	"github.com/seaweedfs/seaweedfs/weed/pb"
	"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
	"google.golang.org/grpc"
)

// LookupRaftLeaderMaster returns the current raft leader by querying configured
// master peers. Followers answer GetMasterConfiguration with the leader address;
// topology mutations such as VolumeMarkReadonly must run on that node.
//
// The volume server's heartbeat peer (GetMaster) may still be a follower after an
// election until the heartbeat loop reconnects.
func LookupRaftLeaderMaster(ctx context.Context, masters []pb.ServerAddress, grpcDialOption grpc.DialOption) (pb.ServerAddress, error) {
	if len(masters) == 0 {
		return "", fmt.Errorf("no master peers configured")
	}
	var lastErr error
	for _, peer := range masters {
		if err := ctx.Err(); err != nil {
			return "", err
		}
		// The same timeout ctx drives both the RPC and WithMasterServerClient's
		// connection-invalidation decision, so a self-inflicted 5s timeout leaves
		// callCtx.Err() != nil and does not poison the shared master connection.
		callCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
		var leader pb.ServerAddress
		err := WithMasterServerClient(callCtx, false, peer, grpcDialOption, func(client master_pb.SeaweedClient) error {
			resp, err := client.GetMasterConfiguration(callCtx, &master_pb.GetMasterConfigurationRequest{})
			if err != nil {
				return err
			}
			if resp.Leader == "" {
				return fmt.Errorf("master %s returned empty leader", peer)
			}
			leader = pb.ServerAddress(resp.Leader)
			return nil
		})
		cancel()
		if err == nil && leader != "" {
			return leader, nil
		}
		lastErr = err
	}
	if lastErr != nil {
		return "", fmt.Errorf("lookup raft leader from %d master peer(s): %w", len(masters), lastErr)
	}
	return "", fmt.Errorf("lookup raft leader: no leader from %d master peer(s)", len(masters))
}
