package server

import (
	"sync"
	"time"

	"github.com/tidwall/tile38/internal/collection"
	"github.com/tidwall/tile38/internal/log"
	"github.com/tidwall/tile38/internal/object"
)

const bgExpireDelay = time.Second / 10

// backgroundExpiring deletes expired items from the database.
// It's executes every 1/10 of a second.
func (s *Server) backgroundExpiring(wg *sync.WaitGroup) {
	defer wg.Done()
	s.loopUntilServerStops(bgExpireDelay, func() {
		s.mu.Lock()
		defer s.mu.Unlock()
		now := time.Now()
		s.backgroundExpireObjects(now)
		s.backgroundExpireHooks(now)
	})
}

func (s *Server) backgroundExpireObjects(now time.Time) {
	nano := now.UnixNano()
	var msgs []*Message
	s.cols.Scan(func(key string, col *collection.Collection) bool {
		col.ScanExpires(func(o *object.Object) bool {
			if nano < o.Expires() {
				return false
			}
			msgs = append(msgs, &Message{Args: []string{"del", key, o.ID()}})
			return true
		})
		return true
	})
	for _, msg := range msgs {
		_, d, err := s.cmdDEL(msg)
		if err != nil {
			log.Fatal(err)
		}
		if err := s.writeAOF(msg.Args, &d); err != nil {
			log.Fatal(err)
		}
	}
	if len(msgs) > 0 {
		log.Debugf("Expired %d objects\n", len(msgs))
	}
}

func (s *Server) backgroundExpireHooks(now time.Time) {
	var msgs []*Message
	s.hookExpires.Ascend(nil, func(v interface{}) bool {
		h := v.(*Hook)
		if h.expires.After(now) {
			return false
		}
		msg := &Message{}
		if h.channel {
			msg.Args = []string{"delchan", h.Name}
		} else {
			msg.Args = []string{"delhook", h.Name}
		}
		msgs = append(msgs, msg)
		return true
	})

	for _, msg := range msgs {
		_, d, err := s.cmdDelHook(msg)
		if err != nil {
			log.Fatal(err)
		}
		if err := s.writeAOF(msg.Args, &d); err != nil {
			log.Fatal(err)
		}
	}
	if len(msgs) > 0 {
		log.Debugf("Expired %d hooks\n", len(msgs))
	}
}