package probe

import (
	"context"
	"fmt"
	"sync"

	cdr "git.loafle.net/commons/di-go/registry"
	occa "git.loafle.net/overflow/commons-go/core/annotation"
	occi "git.loafle.net/overflow/commons-go/core/interfaces"

	"git.loafle.net/overflow/probe/service"
)

type Probe struct {
	services []interface{}

	endChan  chan error
	stopChan chan struct{}
	stopWg   sync.WaitGroup
}

func (p *Probe) EndableStart() (<-chan error, error) {
	if p.stopChan != nil {
		return nil, fmt.Errorf("already running. Stop it before starting it again")
	}

	services, err := cdr.GetInstancesByAnnotationType(occa.RPCServiceAnnotationType)
	if nil != err {
		return nil, err
	}
	p.services = services

	if err := occi.ExecServices(p.services, occi.ServiceMethodInit, service.OrderedServices, false); nil != err {
		return nil, err
	}

	p.endChan = make(chan error)
	p.stopChan = make(chan struct{})

	p.stopWg.Add(1)
	go p.handleProbe()

	return p.endChan, nil
}

func (p *Probe) Stop(ctx context.Context) error {
	if p.stopChan == nil {
		return nil
	}
	close(p.stopChan)
	p.stopWg.Wait()

	occi.ExecServices(p.services, occi.ServiceMethodDestroy, service.OrderedServices, true)

	p.stopChan = nil
	close(p.endChan)

	return nil
}

func (p *Probe) handleProbe() {
	var err error
	defer func() {
		p.stopWg.Done()
		p.endChan <- err
	}()

	err = occi.ExecServices(p.services, occi.ServiceMethodStart, service.OrderedServices, false)
	if nil != err {
		return
	}

LOOP:
	for {
		select {
		case <-p.stopChan:
			break LOOP
		}
	}

	occi.ExecServices(p.services, occi.ServiceMethodStop, service.OrderedServices, true)
}
