package slprober

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/pingcap/log"
	opv2 "github.com/pingcap/tidb-operator/api/v2/core/v1alpha1"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	tcinformers "github.com/pingcap/tidb-operator/pkg/client/informers/externalversions"
	tclistersv1alpha1 "github.com/pingcap/tidb-operator/pkg/client/listers/pingcap/v1alpha1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/sli-reporter/slprober/handshake"

	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
)

type ServiceStatus string
type TidbClusterStatus string

const (
	TidbClusterCreating TidbClusterStatus = "CREATING"

	// for billing system
	TiDBClusterClusterIDLabel    = "billing-cluster-id"
	TiDBClusterStatusFromCentral = "billing-cluster-status-from-central"

	// for dev/staging env
	TiDBClusterClusterIDLabelAlt = "cluster"
)

const (
	ServiceStatusSkippedByNotReady ServiceStatus = "skipped_by_not_ready"
	ServiceStatusSkippedBySuspend  ServiceStatus = "skipped_by_suspend"
	ServiceStatusSkipByOnlyCDC     ServiceStatus = "skipped_by_only_cdc"
	ServiceStatusDetectedAsDead    ServiceStatus = "dead"
	ServiceStatusDetectedAsAlive   ServiceStatus = "alive"
)

const (
	ProbeTimeout = time.Second * 5
)

type ServiceLevelProber struct {
	kubeCli  kubernetes.Interface
	tcLister tclistersv1alpha1.TidbClusterLister
	cli      client.Client
}

func NewServiceLevelProber(
	kubeCli kubernetes.Interface,
	cli client.Client,
	informerFactory tcinformers.SharedInformerFactory) *ServiceLevelProber {
	return &ServiceLevelProber{
		cli:      cli,
		kubeCli:  kubeCli,
		tcLister: informerFactory.Pingcap().V1alpha1().TidbClusters().Lister(),
	}
}

func (m *ServiceLevelProber) Probe() {
	ctx, cancel := context.WithTimeout(context.Background(), ProbeTimeout)
	defer cancel()
	log.Info("begin probe clusters")

	timeBegin := time.Now()

	// list tidbGroup
	var tidbGroupList opv2.TiDBGroupList
	err := m.cli.List(context.TODO(), &tidbGroupList, &client.ListOptions{Namespace: ""})
	if err != nil {
		log.Warn("probe clusters, list tidbGroup failed",
			zap.Error(err))
	}
	// list tidbCluster
	tidbClusters, err := m.tcLister.List(labels.Everything())
	if err != nil {
		log.Warn("probe clusters, list tc failed",
			zap.Error(err))
	}

	timeListDuration := time.Since(timeBegin)
	if len(tidbGroupList.Items) == 0 && len(tidbClusters) == 0 {
		log.Warn("finish probe clusters: no tc and tidbGroup",
			zap.Duration("listElapsed", timeListDuration))
		return
	}

	handshakeCli := &handshake.MysSQLImpl{}
	// Let's ping all clusters concurrently
	if len(tidbGroupList.Items) > 0 {
		m.probeNextGen(ctx, &tidbGroupList, timeBegin, timeListDuration, handshakeCli)
	}
	if len(tidbClusters) > 0 {
		m.probeDedicated(ctx, tidbClusters, timeBegin, timeListDuration, handshakeCli)
	}
}

func (m *ServiceLevelProber) probeDedicated(
	ctx context.Context,
	tidbClusters []*v1alpha1.TidbCluster,
	timeBegin time.Time,
	timeListDuration time.Duration,
	handshakeCli handshake.Interface,
) {
	timeProbeBegin := time.Now()
	probeResultsCh := make(chan clusterResult, len(tidbClusters))
	var wg sync.WaitGroup
	clusterId2TcList := make(map[string][]*v1alpha1.TidbCluster)
	for _, tc := range tidbClusters {
		if result, err := IsTiCDCTC(tc); err != nil {
			log.Warn("probe clusters, check ticdc tc failed, use this tc as normal tc",
				zap.String("tc", fmt.Sprintf("%s/%s", tc.Namespace, tc.Name)),
				zap.Error(err))
		} else if result {
			log.Info("probe clusters, skip ticdc tc",
				zap.String("tc", fmt.Sprintf("%s/%s", tc.Namespace, tc.Name)))
			continue
		}
		clusterID := getClusterID(tc)
		if _, ok := clusterId2TcList[clusterID]; !ok {
			clusterId2TcList[clusterID] = make([]*v1alpha1.TidbCluster, 0)
		}
		clusterId2TcList[clusterID] = append(clusterId2TcList[clusterID], tc)
	}
	for _, tcList := range clusterId2TcList {
		wg.Add(1)
		go m.probeDedicatedCluster(ctx, tcList, &wg, probeResultsCh, handshakeCli)
	}
	wg.Wait()
	close(probeResultsCh)

	go func() {
		// Generate statistics summary for output
		aliveClusters := make([]string, 0)
		deadClusters := make([]string, 0)
		skipClusters := make([]string, 0)
		for r := range probeResultsCh {
			switch r.status {
			case ServiceStatusDetectedAsAlive:
				aliveClusters = append(aliveClusters, r.clusterID)
			case ServiceStatusDetectedAsDead:
				deadClusters = append(deadClusters, r.clusterID)
			case ServiceStatusSkippedByNotReady, ServiceStatusSkippedBySuspend, ServiceStatusSkipByOnlyCDC:
				skipClusters = append(skipClusters, r.clusterID)
			}
		}
		timeProbeDuration := time.Since(timeProbeBegin)
		timeDuration := time.Since(timeBegin)
		log.Info("finish probe clusters",
			zap.Int("allClusters", len(clusterId2TcList)),
			zap.Int("aliveClusters", len(aliveClusters)),
			zap.Int("deadClusters", len(deadClusters)),
			zap.Int("skipClusters", len(skipClusters)),
			zap.Duration("listElapsed", timeListDuration),
			zap.Duration("probeElapsed", timeProbeDuration),
			zap.Duration("totalElapsed", timeDuration),
			zap.Any("deadClusterDetail", deadClusters),
		)
	}()
}

func (m *ServiceLevelProber) probeNextGen(
	ctx context.Context,
	tidbGroupList *opv2.TiDBGroupList,
	timeBegin time.Time,
	timeListDuration time.Duration,
	handshakeCli handshake.Interface,
) {
	timeProbeBegin := time.Now()
	probeResultsCh := make(chan clusterResult, len(tidbGroupList.Items)) // TODO: len(tidbGroupList.Items)?
	var wg sync.WaitGroup

	log.Info("Begin probe next-gen clusters")
	// classify tidbGroup by cluster id
	clusterId2TiDBGroup := make(map[string][]*opv2.TiDBGroup)
	for i := range tidbGroupList.Items {
		tidbGroup := tidbGroupList.Items[i]
		if strings.HasSuffix(tidbGroup.Name, "-worker") {
			continue
		}
		clusterID := getClusterIDFromTiDBGroup(&tidbGroup)
		if _, ok := clusterId2TiDBGroup[clusterID]; !ok {
			clusterId2TiDBGroup[clusterID] = make([]*opv2.TiDBGroup, 0)
		}
		clusterId2TiDBGroup[clusterID] = append(clusterId2TiDBGroup[clusterID], &tidbGroup)
	}
	// probe for every cluster
	for _, tgs := range clusterId2TiDBGroup {
		tgs := tgs
		wg.Add(1)
		go m.probeNextGenCluster(ctx, tgs, &wg, probeResultsCh, handshakeCli)
	}
	wg.Wait()
	close(probeResultsCh)

	go func() {
		// Generate statistics summary for output
		aliveClusters := make([]string, 0)
		deadClusters := make([]string, 0)
		skipClusters := make([]string, 0)
		for r := range probeResultsCh {
			switch r.status {
			case ServiceStatusDetectedAsAlive:
				aliveClusters = append(aliveClusters, r.clusterID)
			case ServiceStatusDetectedAsDead:
				deadClusters = append(deadClusters, r.clusterID)
			case ServiceStatusSkippedByNotReady, ServiceStatusSkippedBySuspend, ServiceStatusSkipByOnlyCDC:
				skipClusters = append(skipClusters, r.clusterID)
			}
		}
		timeProbeDuration := time.Since(timeProbeBegin)
		timeDuration := time.Since(timeBegin)
		log.Info("finish probe next-gen clusters",
			zap.Int("allClusters", len(clusterId2TiDBGroup)),
			zap.Int("aliveClusters", len(aliveClusters)),
			zap.Int("deadClusters", len(deadClusters)),
			zap.Int("skipClusters", len(skipClusters)),
			zap.Duration("listElapsed", timeListDuration),
			zap.Duration("probeElapsed", timeProbeDuration),
			zap.Duration("totalElapsed", timeDuration),
			zap.Any("deadClusterDetail", deadClusters),
		)
	}()
}

type clusterResult struct {
	status    ServiceStatus
	clusterID string
}

// probeDedicatedCluster probes a dedicated cluster, it may contain multiple TiDBCluster(tc)
// it will probe all tc in the same cluster,
// if one of them is alive, it will return alive
// if all of them are dead, it will return dead
func (m *ServiceLevelProber) probeDedicatedCluster(
	ctx context.Context,
	tcList []*v1alpha1.TidbCluster,
	wg *sync.WaitGroup,
	resultCh chan<- clusterResult,
	handshakeCli handshake.Interface,
) {
	defer wg.Done()

	clusterID := getClusterID(tcList[0])
	timeBegin := time.Now()
	logger := log.L().With(zap.String("clusterID", clusterID))
	logger.Info("begin probe cluster", zap.Int("tcCount", len(tcList)))

	skipProbe, status := m.precheckProbeDedicatedCluster(ctx, tcList)
	if skipProbe {
		logger.Info("skip probe cluster", zap.String("status", string(status)))
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}

	svcList, err := m.kubeCli.CoreV1().Services(tcList[0].Namespace).List(ctx, metav1.ListOptions{
		LabelSelector: "app.kubernetes.io/used-by=end-user,app.kubernetes.io/name=tidb-cluster,app.kubernetes.io/managed-by=tidb-operator",
	})
	if err != nil {
		status = ServiceStatusDetectedAsDead
		logger.Error("list services failed", zap.Error(err),
			zap.Duration("elapsed", time.Since(timeBegin)),
		)
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}
	svcMap := make(map[string]*v1.Service)
	for i := range svcList.Items {
		svc := svcList.Items[i]
		svcMap[svc.Name] = &svc
	}

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	tcResultChan := make(chan tcResult, len(tcList))
	for _, tc := range tcList {
		tidbSvc, ok := svcMap[tc.Name+"-tidb"]
		if !ok {
			status = ServiceStatusDetectedAsDead
			logger.Error("tidb service not found", zap.String("tcName", tc.Name))
			m.postProbeCluster(clusterID, status, resultCh)
			return
		}
		tiproxySvc := svcMap[tc.Name+"-tiproxy"]
		go probeTiDBCluster(ctx, tc, tidbSvc, tiproxySvc, tcResultChan, handshakeCli)
	}

	resultCount := 0
	for {
		select {
		case <-ctx.Done():
			logger.Info("finish probe cluster, context done, mark as dead",
				zap.Duration("elapsed", time.Since(timeBegin)),
			)
			m.postProbeCluster(clusterID, ServiceStatusDetectedAsDead, resultCh)
			return
		case result := <-tcResultChan:
			resultCount++
			if result.success {
				// once we get an alive result, we can cancel the other goroutines
				logger.Info("finish probe cluster, detected as alive",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				m.postProbeCluster(clusterID, ServiceStatusDetectedAsAlive, resultCh)
				return
			}
			if resultCount >= len(tcList) {
				// all tc results are dead
				logger.Info("finish probe cluster, detected as dead",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				m.postProbeCluster(clusterID, ServiceStatusDetectedAsDead, resultCh)
				return
			}
		}
	}
}

// probeNextGenCluster probes a next-gen cluster, it contains several TiDBGroups and a TiProxyGroup
// it will probe the TiDBGroups and TiProxyGroup in the same cluster,
// if all of them is alive, it will return alive
// if one of them are dead, it will return dead
func (m *ServiceLevelProber) probeNextGenCluster(
	ctx context.Context,
	tidbGroups []*opv2.TiDBGroup,
	wg *sync.WaitGroup,
	resultCh chan<- clusterResult,
	handshakeCli handshake.Interface,
) {
	defer wg.Done()

	clusterID := getClusterIDFromTiDBGroup(tidbGroups[0])
	timeBegin := time.Now()
	logger := log.L().With(zap.String("clusterID", clusterID))
	logger.Info("begin probe next-gen cluster", zap.Int("tidbGroupCount", len(tidbGroups)))

	skipProbe, status := m.precheckProbeNextgenCluster(tidbGroups)
	if skipProbe {
		logger.Info("skip probe next-gen cluster", zap.String("status", string(status)))
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}

	// list tidbGroup svc
	// each TiDBGroup has a svc
	// list svc by each TiDBGroup's group id
	// if len of svc is 0, return dead status
	status = ServiceStatusDetectedAsDead
	tidbSvcItems := make([]v1.Service, 0)
	for _, tidbGroup := range tidbGroups {
		tidbSvcList, err := m.kubeCli.CoreV1().Services(tidbGroup.Namespace).List(ctx, metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", LabelGroup, tidbGroup.Name),
		})
		if err != nil {
			status = ServiceStatusDetectedAsDead
			logger.Error("probe next-gen clusters, list tidbGroups services failed",
				zap.String("tidbGroup", tidbGroup.Name),
				zap.Error(err),
			)
		} else {
			status = ServiceStatusDetectedAsAlive
			tidbSvcItems = append(tidbSvcItems, tidbSvcList.Items...)
		}
	}
	// if there is no TiDBGroup svc, return dead
	if status == ServiceStatusDetectedAsDead && len(tidbSvcItems) == 0 {
		logger.Error("probe next-gen clusters, list tidbGroups services failed: no svc found",
			zap.Duration("elapsed", time.Since(timeBegin)),
		)
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}

	tidbSvcMap := make(map[string]*v1.Service)
	for i := range tidbSvcItems {
		svc := tidbSvcItems[i]
		tidbSvcMap[svc.Name] = &svc
	}

	// get tiproxyGroup and svc for this cluster id
	// todo: how to get tiproxygroups of azure/gcp dedicated cluster in the future
	var tiproxyGroupList opv2.TiProxyGroupList
	labelSelector := metav1.LabelSelector{
		MatchLabels: map[string]string{
			LabelCluster: clusterID,
		},
	}
	selector, err := metav1.LabelSelectorAsSelector(&labelSelector)
	if err != nil {
		status = ServiceStatusDetectedAsDead
		logger.Error("probe next-gen clusters, list tiproxyGroup failed", zap.Error(err),
			zap.Duration("elapsed", time.Since(timeBegin)),
		)
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}

	enableTiProxy := true
	err = m.cli.List(ctx, &tiproxyGroupList, &client.ListOptions{LabelSelector: selector})
	if err != nil {
		status = ServiceStatusDetectedAsDead
		logger.Error("probe next-gen clusters, list tiproxyGroup failed",
			zap.Error(err))
		m.postProbeCluster(clusterID, status, resultCh)
		return
	}
	if len(tiproxyGroupList.Items) == 0 {
		enableTiProxy = false
	}

	var tiproxyGroup opv2.TiProxyGroup
	var tiproxySvc *v1.Service
	if enableTiProxy {
		// each cluster has only one TiProxyGroup
		tiproxyGroup = tiproxyGroupList.Items[0]
		// get tiproxygroup's svc by group id
		tiproxySvcList, err := m.kubeCli.CoreV1().Services(tiproxyGroup.Namespace).List(ctx, metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", LabelGroup, tiproxyGroup.Name),
		})
		if err != nil {
			status = ServiceStatusDetectedAsDead
			logger.Error("probe next-gen clusters, list tiproxyGroup service failed",
				zap.Error(err))
			m.postProbeCluster(clusterID, status, resultCh)
			return
		}
		// if there is no tiproxy svc, set enableTiProxy to false
		if len(tiproxySvcList.Items) == 0 {
			logger.Warn("no services found for TiProxyGroup", zap.String("group", tiproxyGroup.Name))
			enableTiProxy = false
		} else {
			// get TiProxyGroup's svc
			for i := range tiproxySvcList.Items {
				svc := tiproxySvcList.Items[i]
				if svc.Name == fmt.Sprintf("%s-tiproxy", tiproxyGroup.Name) {
					tiproxySvc = &svc
					break
				}
			}
			if tiproxySvc == nil {
				logger.Warn("target TiProxyGroup service not found",
					zap.String("group", tiproxyGroup.Name))
				enableTiProxy = false
			}
		}
	}

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	probeCount := 1
	if enableTiProxy {
		probeCount++
	}
	probeResultChan := make(chan ServiceStatus, probeCount)

	go m.probeTiDBGroups(
		ctx,
		tidbGroups,
		tidbSvcMap,
		probeResultChan,
		handshakeCli,
		logger,
		timeBegin,
	)

	if enableTiProxy {
		go m.probeTiProxyGroup(
			ctx,
			&tiproxyGroup,
			tiproxySvc,
			probeResultChan,
			handshakeCli,
			logger,
			timeBegin,
		)
	}

	resultCount := 0
	for {
		select {
		case <-ctx.Done():
			logger.Info("finish probe next-gen cluster, context done, mark as dead",
				zap.Duration("elapsed", time.Since(timeBegin)),
			)
			m.postProbeCluster(clusterID, ServiceStatusDetectedAsDead, resultCh)
			return
		case result := <-probeResultChan:
			resultCount++
			if result == ServiceStatusDetectedAsDead {
				// once we get a dead result, we can cancel the other goroutines
				logger.Info("finish probe next-gen cluster, detected as dead",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				m.postProbeCluster(clusterID, ServiceStatusDetectedAsDead, resultCh)
				return
			}
			if resultCount >= probeCount {
				logger.Info("finish probe next-gen cluster, detected as alive",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				m.postProbeCluster(clusterID, ServiceStatusDetectedAsAlive, resultCh)
				return
			}
		}
	}
}

func (m *ServiceLevelProber) precheckProbeDedicatedCluster(
	ctx context.Context,
	tcList []*v1alpha1.TidbCluster,
) (skipProbe bool, status ServiceStatus) {
	defaultTc := tcList[0]
	for _, tc := range tcList {
		if tc.Name == "db" {
			defaultTc = tc
			break
		}
	}
	clusterID := getClusterID(defaultTc)
	// Skip creating status
	isCreating, err := IsClusterInCreatingStatus(ctx, m.kubeCli, defaultTc)
	if err != nil {
		log.Warn("precheck probe cluster, get cluster creating status failed",
			zap.String("clusterID", clusterID),
			zap.Error(err))
	}
	if isCreating {
		return true, ServiceStatusSkippedByNotReady
	}
	if defaultTc.Spec.TiDB == nil {
		return true, ServiceStatusSkipByOnlyCDC
	}
	if defaultTc.Status.TiDB.Phase == v1alpha1.SuspendPhase {
		return true, ServiceStatusSkippedBySuspend
	}
	return false, ServiceStatusDetectedAsDead
}

func (m *ServiceLevelProber) precheckProbeNextgenCluster(
	tgs []*opv2.TiDBGroup,
) (skipProbe bool, status ServiceStatus) {
	ready := false
	for i := range tgs {
		tg := tgs[i]
		if tg.Status.ReadyReplicas > 0 {
			ready = true
			break
		}
	}
	if !ready {
		return true, ServiceStatusSkippedByNotReady
	}

	return false, ServiceStatusDetectedAsDead
}

func (m *ServiceLevelProber) postProbeCluster(
	clusterID string,
	status ServiceStatus,
	resultCh chan<- clusterResult,
) {
	ProbeClusterCounter.
		WithLabelValues(string(status), fmt.Sprintf("tidb%s", clusterID), clusterID).
		Inc()
	resultCh <- clusterResult{status: status, clusterID: clusterID}
}

func (m *ServiceLevelProber) probeTiDBGroups(
	ctx context.Context,
	tidbGroups []*opv2.TiDBGroup,
	tidbSvcMap map[string]*v1.Service,
	resultChan chan<- ServiceStatus,
	handshakeCli handshake.Interface,
	logger *zap.Logger,
	timeBegin time.Time,
) {
	logger.Info("begin probe next-gen cluster's TiDBGroups")
	tidbGroupResultChan := make(chan tidbGroupResult, len(tidbGroups))
	// probe every tidbgroup's svc
	for _, tg := range tidbGroups {
		tg := tg
		tidbSvc, ok := tidbSvcMap[tg.Name+"-tidb"]
		if !ok {
			logger.Error("next-gen cluster tidb service not found", zap.String("tidbGroup", tg.Name))
			tidbGroupResultChan <- tidbGroupResult{
				tidbGroupName: tg.Name,
				success:       false,
			}
			continue
		}
		go probeNextGenSvc(ctx,
			getClusterIDFromTiDBGroup(tg),
			tg.Name,
			"tidbGroup",
			tidbSvc,
			tidbGroupResultChan,
			handshakeCli,
		)
	}

	resultCount := 0
	for {
		select {
		case <-ctx.Done():
			logger.Info("finish probe next-gen cluster's TiDBGroups, context done, mark as dead",
				zap.Duration("elapsed", time.Since(timeBegin)),
			)
			return
		case result := <-tidbGroupResultChan:
			resultCount++
			if result.success {
				// once we get an alive result, we can cancel the other goroutines
				logger.Info("finish probe next-gen cluster's TiDBGroups, detected as alive",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				resultChan <- ServiceStatusDetectedAsAlive
				return
			}
			if resultCount >= len(tidbGroups) {
				// all tidbgroups results are dead
				logger.Info("finish probe next-gen cluster's TiDBGroups, detected as dead",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				resultChan <- ServiceStatusDetectedAsDead
				return
			}
		}
	}
}

func (m *ServiceLevelProber) probeTiProxyGroup(
	ctx context.Context,
	tiproxyGroup *opv2.TiProxyGroup,
	tiproxySvc *v1.Service,
	resultChan chan<- ServiceStatus,
	handshakeCli handshake.Interface,
	logger *zap.Logger,
	timeBegin time.Time,
) {
	logger.Info("begin probe cluster's TiProxyGroup")
	tidbGroupResultChan := make(chan tidbGroupResult, 1)

	go probeNextGenSvc(
		ctx,
		getClusterIDFromTiProxyGroup(tiproxyGroup),
		tiproxyGroup.Name,
		"tiproxyGroup",
		tiproxySvc,
		tidbGroupResultChan,
		handshakeCli,
	)

	resultCount := 0
	for {
		select {
		case <-ctx.Done():
			logger.Info("finish probe next-gen cluster's TiProxyGroup, context done, mark as dead",
				zap.Duration("elapsed", time.Since(timeBegin)),
			)
			return
		case result := <-tidbGroupResultChan:
			resultCount++
			if result.success {
				// once we get an alive result, we can cancel the other goroutines
				logger.Info("finish probe next-gen cluster's TiProxyGroup, detected as alive",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				resultChan <- ServiceStatusDetectedAsAlive
				return
			}
			if resultCount >= 1 {
				logger.Info("finish probe next-gen cluster's TiProxyGroup, detected as dead",
					zap.Duration("elapsed", time.Since(timeBegin)),
				)
				resultChan <- ServiceStatusDetectedAsDead
				return
			}
		}
	}
}
