package subscriptions

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

type SubscriptionID string

type Subscription struct {
	mu      sync.RWMutex
	id      SubscriptionID
	signal  *filterSignal
	quit    chan struct{}
	filter  filter
	started bool
}

func NewSubscription(namespace string, filter filter) *Subscription {
	subscriptionID := NewSubscriptionID(namespace, filter.getID())
	return &Subscription{
		id:     subscriptionID,
		signal: newFilterSignal(string(subscriptionID)),
		filter: filter,
	}
}

func (s *Subscription) Start(checkPeriod time.Duration) error {
	s.mu.Lock()
	if s.started {
		s.mu.Unlock()
		return errors.New("subscription already started or used")
	}
	s.started = true
	s.quit = make(chan struct{})
	quit := s.quit
	s.mu.Unlock()

	ticker := time.NewTicker(checkPeriod)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			filterData, err := s.filter.getChanges()
			if err != nil {
				s.signal.SendError(err)
			} else if len(filterData) > 0 {
				s.signal.SendData(filterData)
			}
		case <-quit:
			return nil
		}
	}
}

func (s *Subscription) Stop(uninstall bool) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if !s.started {
		return nil
	}
	select {
	case _, ok := <-s.quit:
		// handle a case of a closed channel
		if !ok {
			return nil
		}
	default:
		close(s.quit)
	}
	if !uninstall {
		return nil
	}
	return s.filter.uninstall()
}

func NewSubscriptionID(namespace, filterID string) SubscriptionID {
	return SubscriptionID(fmt.Sprintf("%s-%s", namespace, filterID))
}