package main

import (
	"time"

	"github.com/hrfee/mediabrowser"
)

type userDaemon struct {
	Stopped         bool
	ShutdownChannel chan string
	Interval        time.Duration
	period          time.Duration
	app             *appContext
}

func newUserDaemon(interval time.Duration, app *appContext) *userDaemon {
	return &userDaemon{
		Stopped:         false,
		ShutdownChannel: make(chan string),
		Interval:        interval,
		period:          interval,
		app:             app,
	}
}

func (rt *userDaemon) run() {
	rt.app.info.Println("User daemon started")
	for {
		select {
		case <-rt.ShutdownChannel:
			rt.ShutdownChannel <- "Down"
			return
		case <-time.After(rt.period):
			break
		}
		started := time.Now()
		rt.app.checkUsers()
		finished := time.Now()
		duration := finished.Sub(started)
		rt.period = rt.Interval - duration
	}
}

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

func (app *appContext) checkUsers() {
	if err := app.storage.loadUsers(); err != nil {
		app.err.Printf("Failed to load user expiries: %v", err)
		return
	}
	app.storage.usersLock.Lock()
	defer app.storage.usersLock.Unlock()
	if len(app.storage.users) == 0 {
		return
	}
	app.info.Println("Daemon: Checking for user expiry")
	users, status, err := app.jf.GetUsers(false)
	if err != nil || status != 200 {
		app.err.Printf("Failed to get users (%d): %s", status, err)
		return
	}
	mode := "disable"
	termPlural := "Disabling"
	if app.config.Section("user_expiry").Key("behaviour").MustString("disable_user") == "delete_user" {
		mode = "delete"
		termPlural = "Deleting"
	}
	contact := false
	if messagesEnabled && app.config.Section("user_expiry").Key("send_email").MustBool(true) {
		contact = true
	}
	// Use a map to speed up checking for deleted users later
	userExists := map[string]bool{}
	for _, user := range users {
		userExists[user.ID] = true
	}
	for id, expiry := range app.storage.users {
		if _, ok := userExists[id]; !ok {
			app.info.Printf("Deleting expiry for non-existent user \"%s\"", id)
			delete(app.storage.users, id)
		} else if time.Now().After(expiry) {
			found := false
			var user mediabrowser.User
			for _, u := range users {
				if u.ID == id {
					found = true
					user = u
					break
				}
			}
			if !found {
				app.info.Printf("Expired user already deleted, ignoring.")
				delete(app.storage.users, id)
				continue
			}
			app.info.Printf("%s expired user \"%s\"", termPlural, user.Name)
			if mode == "delete" {
				status, err = app.jf.DeleteUser(id)
			} else if mode == "disable" {
				user.Policy.IsDisabled = true
				// Admins can't be disabled
				user.Policy.IsAdministrator = false
				status, err = app.jf.SetPolicy(id, user.Policy)
			}
			if !(status == 200 || status == 204) || err != nil {
				app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err)
				continue
			}
			delete(app.storage.users, id)
			app.jf.CacheExpiry = time.Now()
			if contact {
				if !ok {
					continue
				}
				name := app.getAddressOrName(user.ID)
				msg, err := app.email.constructUserExpired(app, false)
				if err != nil {
					app.err.Printf("Failed to construct expiry message for \"%s\": %s", user.Name, err)
				} else if err := app.sendByID(msg, user.ID); err != nil {
					app.err.Printf("Failed to send expiry message to \"%s\": %s", name, err)
				} else {
					app.info.Printf("Sent expiry notification to \"%s\"", name)
				}
			}
		}
	}
	err = app.storage.storeUsers()
	if err != nil {
		app.err.Printf("Failed to store user expiries: %s", err)
	}
}