package state

import (
	"sync"
	"time"

	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
)

type changer interface {
	Open(*validation.Report) error
	ChangeStatus(*validation.Report, *validation.Report) error
	Renotify(*validation.Report) error
	Resolve(*validation.Report, *validation.Report) error
}

type Tracker struct {
	deduplicationInterval time.Duration
	minOccurances         int
	state                 map[string]*validation.Report
	occuranceCounter      map[string]int
	callbacks             changer
	mutex                 sync.Mutex
}

func NewTracker(callbacks changer, deduplicationInterval time.Duration, minOccurances int) *Tracker {
	return &Tracker{
		state:                 make(map[string]*validation.Report),
		occuranceCounter:      make(map[string]int),
		callbacks:             callbacks,
		deduplicationInterval: deduplicationInterval,
		minOccurances:         minOccurances,
	}
}

func (s *Tracker) Handle(report *validation.Report) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	existingReport, found := s.state[report.Item.ID()]

	if report.Status == validation.StatusOk {
		if found {
			err := s.callbacks.Resolve(existingReport, report)
			if err != nil {
				return err
			}
		}
		delete(s.state, report.Item.ID())
		delete(s.occuranceCounter, report.Item.ID())
		return nil
	}

	s.occuranceCounter[report.Item.ID()] += 1
	count := s.occuranceCounter[report.Item.ID()]
	if count < s.minOccurances {
		return nil
	}

	var err error
	if !found {
		err = s.callbacks.Open(report)
	} else if report.Status != existingReport.Status {
		err = s.callbacks.ChangeStatus(existingReport, report)
	} else if report.Timestamp.Sub(existingReport.Timestamp) > s.deduplicationInterval {
		err = s.callbacks.Renotify(report)
	}

	if err != nil {
		return err
	}

	s.state[report.Item.ID()] = report
	return nil
}
