// Copyright 2024 schukai GmbH
// SPDX-License-Identifier: proprietary

package watch

import (
	"context"
	"sync"
	"time"
)

type Watcher struct {
	cache  sync.Map
	mutex  sync.Mutex
	cancel context.CancelFunc
	active bool
}

// NewWatcher creates a new Watcher.
// This Function starts a goroutine that checks the cache every hour and removes expired entries.
// It is in your responsibility to call Stop() on the Watcher, after you are done with it.
func NewWatcher() *Watcher {
	ctx, cancel := context.WithCancel(context.Background())

	w := &Watcher{
		cache:  sync.Map{},
		cancel: cancel,
		mutex:  sync.Mutex{},
		active: true,
	}

	go w.cacheCleaner(ctx)
	return w
}

// Stop stops the cleaner goroutine.
func (w *Watcher) Stop() {
	w.cancel()
}

// IsActive returns true if the watcher is active, false if not.
// An not active watcher can not be used to add, remove or check files.
func (w *Watcher) IsActive() bool {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	return w.active
}

func (w *Watcher) setActive() {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.active = true
}

func (w *Watcher) setInactive() {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.active = false
}

// cacheCleaner is a goroutine that checks the cache every hour and removes expired entries.
func (w *Watcher) cacheCleaner(ctx context.Context) {
	ticker := time.NewTicker(1 * time.Hour)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			now := time.Now()
			w.cache.Range(func(key, value interface{}) bool {
				hash := value.(FileHash)
				if hash.expiresAt.Before(now) {
					w.cache.Delete(key)
				}
				return true
			})
		case <-ctx.Done():
			w.setInactive()
			return
		}
	}
}