package clock

import (
	"sync"
	"time"
)

// Clock allows tests to control the current time that the server sees.
//
// Clock is necessary because Raids creates background goroutines that do different
// things based on the current time. E.g. There is a goroutine that will start the
// raid when a particular amount of time has passed.
type Clock interface {
	// Returns the current time.
	Now() time.Time
}

// RealClock is meant to be used in Production. It returns the real time.
type RealClock struct{}

var _ Clock = &RealClock{}

func NewRealClock() *RealClock {
	return &RealClock{}
}

func (c *RealClock) Now() time.Time {
	return time.Now()
}

// TestClock is meant to be injected into the server code by test code.
// TestClock provides other methods that the tests can use to modify the current time.
type TestClock struct {
	mutex sync.Mutex
	now   time.Time
}

var _ Clock = &TestClock{}

func NewTestClock() *TestClock {
	return &TestClock{
		now: time.Now(), // Set the default time to the current time
	}
}

func (c *TestClock) Now() time.Time {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	return c.now
}

func (c *TestClock) Add(duration time.Duration) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.now = c.now.Add(duration)
}

func (c *TestClock) Set(now time.Time) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.now = now
}
