/*
 *
 * Copyright 2024 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package health

import (
	
	

	
	
	
	
	
	
)

func () {
	producerBuilderSingleton = &producerBuilder{}
	internal.RegisterClientHealthCheckListener = registerClientSideHealthCheckListener
}

type producerBuilder struct{}

var producerBuilderSingleton *producerBuilder

// Build constructs and returns a producer and its cleanup function.
func (*producerBuilder) ( any) (balancer.Producer, func()) {
	 := &healthServiceProducer{
		cc:     .(grpc.ClientConnInterface),
		cancel: func() {},
	}
	return , func() {
		.mu.Lock()
		defer .mu.Unlock()
		.cancel()
	}
}

type healthServiceProducer struct {
	// The following fields are initialized at build time and read-only after
	// that and therefore do not need to be guarded by a mutex.
	cc grpc.ClientConnInterface

	mu     sync.Mutex
	cancel func()
}

// registerClientSideHealthCheckListener accepts a listener to provide server
// health state via the health service.
func ( context.Context,  balancer.SubConn,  string,  func(balancer.SubConnState)) func() {
	,  := .GetOrBuildProducer(producerBuilderSingleton)
	 := .(*healthServiceProducer)
	.mu.Lock()
	defer .mu.Unlock()
	.cancel()
	if  == nil {
		return 
	}

	,  := context.WithCancel()
	.cancel = 

	go .startHealthCheck(, , , )
	return 
}

func ( *healthServiceProducer) ( context.Context,  balancer.SubConn,  string,  func(balancer.SubConnState)) {
	 := func( string) (any, error) {
		return .cc.NewStream(, &grpc.StreamDesc{ServerStreams: true}, )
	}

	 := func( connectivity.State,  error) {
		(balancer.SubConnState{
			ConnectivityState: ,
			ConnectionError:   ,
		})
	}

	// Call the function through the internal variable as tests use it for
	// mocking.
	 := internal.HealthCheckFunc(, , , )
	if  == nil {
		return
	}
	if status.Code() == codes.Unimplemented {
		logger.Errorf("Subchannel health check is unimplemented at server side, thus health check is disabled for SubConn %p", )
	} else {
		logger.Errorf("Health checking failed for SubConn %p: %v", , )
	}
}