package controller

import (
	"context"
	"errors"
	"fmt"
	"sync"
)

type NotifierService interface {
	RunFetchEvents(ctx context.Context) error
}

type Controller struct {
	notifier NotifierService

	wg     sync.WaitGroup
	errCh  chan error
	cancel context.CancelFunc
}

func NewController(notifierService NotifierService) *Controller {
	return &Controller{
		notifier: notifierService,
		wg:       sync.WaitGroup{},
		errCh:    make(chan error, 1),
	}
}

// Run service asynchroniously.
func (c *Controller) Run(ctx context.Context) {
	ctx, cancel := context.WithCancel(ctx)
	c.cancel = cancel

	c.wg.Add(1)
	go func() {
		defer c.wg.Done()

		if err := c.notifier.RunFetchEvents(ctx); err != nil && !errors.Is(err, context.Canceled) {
			select {
			case c.errCh <- fmt.Errorf("RunFetchEvents: %w", err):
			default:
			}
		}
	}()
}

// Gracefully stop service.
func (c *Controller) Stop(ctx context.Context) error {
	if c.cancel != nil {
		c.cancel()
	}

	done := make(chan struct{})
	go func() {
		c.wg.Wait()
		close(done)
	}()

	select {
	case <-done:
	case <-ctx.Done():
		return ctx.Err()
	}

	close(c.errCh)

	select {
	case err := <-c.errCh:
		return err
	default:
		return nil
	}
}
