package main

import "time"

// clearEmails removes stored emails for users which no longer exist.
// meant to be called with other such housekeeping functions, so assumes
// the user cache is fresh.
func (app *appContext) clearEmails() {
	app.debug.Println("Housekeeping: removing unused email addresses")
	users, status, err := app.jf.GetUsers(false)
	if status != 200 || err != nil || len(users) == 0 {
		app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
		return
	}
	// Rebuild email storage to from existing users to reduce time complexity
	emails := emailStore{}
	app.storage.emailsLock.Lock()
	for _, user := range users {
		if email, ok := app.storage.GetEmailsKey(user.ID); ok {
			emails[user.ID] = email
		}
	}
	app.storage.emails = emails
	app.storage.storeEmails()
	app.storage.emailsLock.Unlock()
}

// clearDiscord does the same as clearEmails, but for Discord Users.
func (app *appContext) clearDiscord() {
	app.debug.Println("Housekeeping: removing unused Discord IDs")
	users, status, err := app.jf.GetUsers(false)
	if status != 200 || err != nil || len(users) == 0 {
		app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
		return
	}
	// Rebuild discord storage to from existing users to reduce time complexity
	dcUsers := discordStore{}
	app.storage.discordLock.Lock()
	for _, user := range users {
		if dcUser, ok := app.storage.GetDiscordKey(user.ID); ok {
			dcUsers[user.ID] = dcUser
		}
	}
	app.storage.discord = dcUsers
	app.storage.storeDiscordUsers()
	app.storage.discordLock.Unlock()
}

// clearMatrix does the same as clearEmails, but for Matrix Users.
func (app *appContext) clearMatrix() {
	app.debug.Println("Housekeeping: removing unused Matrix IDs")
	users, status, err := app.jf.GetUsers(false)
	if status != 200 || err != nil || len(users) == 0 {
		app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
		return
	}
	// Rebuild matrix storage to from existing users to reduce time complexity
	mxUsers := matrixStore{}
	app.storage.matrixLock.Lock()
	for _, user := range users {
		if mxUser, ok := app.storage.GetMatrixKey(user.ID); ok {
			mxUsers[user.ID] = mxUser
		}
	}
	app.storage.matrix = mxUsers
	app.storage.storeMatrixUsers()
	app.storage.matrixLock.Unlock()
}

// clearTelegram does the same as clearEmails, but for Telegram Users.
func (app *appContext) clearTelegram() {
	app.debug.Println("Housekeeping: removing unused Telegram IDs")
	users, status, err := app.jf.GetUsers(false)
	if status != 200 || err != nil || len(users) == 0 {
		app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
		return
	}
	// Rebuild telegram storage to from existing users to reduce time complexity
	tgUsers := telegramStore{}
	app.storage.telegramLock.Lock()
	for _, user := range users {
		if tgUser, ok := app.storage.GetTelegramKey(user.ID); ok {
			tgUsers[user.ID] = tgUser
		}
	}
	app.storage.telegram = tgUsers
	app.storage.storeTelegramUsers()
	app.storage.telegramLock.Unlock()
}

// https://bbengfort.github.io/snippets/2016/06/26/background-work-goroutines-timer.html THANKS

type housekeepingDaemon struct {
	Stopped         bool
	ShutdownChannel chan string
	Interval        time.Duration
	period          time.Duration
	jobs            []func(app *appContext)
	app             *appContext
}

func newInviteDaemon(interval time.Duration, app *appContext) *housekeepingDaemon {
	daemon := housekeepingDaemon{
		Stopped:         false,
		ShutdownChannel: make(chan string),
		Interval:        interval,
		period:          interval,
		app:             app,
	}
	daemon.jobs = []func(app *appContext){func(app *appContext) {
		app.debug.Println("Housekeeping: Checking for expired invites")
		app.checkInvites()
	}}

	clearEmail := app.config.Section("email").Key("require_unique").MustBool(false)
	clearDiscord := app.config.Section("discord").Key("require_unique").MustBool(false)
	clearTelegram := app.config.Section("telegram").Key("require_unique").MustBool(false)
	clearMatrix := app.config.Section("matrix").Key("require_unique").MustBool(false)

	if clearEmail || clearDiscord || clearTelegram || clearMatrix {
		daemon.jobs = append(daemon.jobs, func(app *appContext) { app.jf.CacheExpiry = time.Now() })
	}

	if clearEmail {
		daemon.jobs = append(daemon.jobs, func(app *appContext) { app.clearEmails() })
	}
	if clearDiscord {
		daemon.jobs = append(daemon.jobs, func(app *appContext) { app.clearDiscord() })
	}
	if clearTelegram {
		daemon.jobs = append(daemon.jobs, func(app *appContext) { app.clearTelegram() })
	}
	if clearMatrix {
		daemon.jobs = append(daemon.jobs, func(app *appContext) { app.clearMatrix() })
	}

	return &daemon
}

func (rt *housekeepingDaemon) run() {
	rt.app.info.Println("Invite daemon started")
	for {
		select {
		case <-rt.ShutdownChannel:
			rt.ShutdownChannel <- "Down"
			return
		case <-time.After(rt.period):
			break
		}
		started := time.Now()
		rt.app.storage.loadInvites()

		for _, job := range rt.jobs {
			job(rt.app)
		}

		finished := time.Now()
		duration := finished.Sub(started)
		rt.period = rt.Interval - duration
	}
}

func (rt *housekeepingDaemon) Shutdown() {
	rt.Stopped = true
	rt.ShutdownChannel <- "Down"
	<-rt.ShutdownChannel
	close(rt.ShutdownChannel)
}