package monitor

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"time"

	"code.justin.tv/video/spectre/backend"
	"code.justin.tv/video/spectre/usher"
	"code.justin.tv/video/spectre/util"
)

var monitorScanPeriod = 10 * time.Second
var forceMaster *bool
var dryRun *bool
var usherService usher.Usher
var db backend.MonitorBackend
var isMaster = util.IsMaster

// The Monitor is intended to sync state between Usher and SpectreDB.
//
// It periodically retrieves live channels and spectre channels from Usher,
// and compares it to its local state in SpectreDB. If the channel is
// neither live and the spectre channel is enabled, then we'll initiate
// the stream with Usher. Similarly, if the channel gets disabled, or goes
// live, then we'll tear down the stream with Usher.
//
// Some additional checks are performed to ensure the SpectreDB state is
// consistent with Usher.
func Monitor(b *backend.Backend) {
	fmt.Println("Starting Spectre monitor...")
	util.StartAcquireLockRoutine()
	installSignalHandler()
	if *dryRun {
		fmt.Println("Running in dry-run mode.")
		usherService = usher.UsherDryRun{usher.UsherService{}}
		db = &backend.BackendDryRun{*b}
	} else {
		usherService = usher.UsherService{}
		db = b
	}
	go refreshStreams()
	go refreshViewcounts()
	for {
		runOnce()
		time.Sleep(monitorScanPeriod)
	}
}

func RegisterFlags() {
	forceMaster = flag.Bool("force-master", false, "Force the Spectre monitor to run in master mode")
	dryRun = flag.Bool("dry-run", false, "Run the Spectre monitor in dry-run mode")
}

func installSignalHandler() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		fmt.Println("Spectre monitor shutting down...")
		os.Exit(0)
	}()
}

func runOnce() {
	if isMaster() || *forceMaster {
		liveChannels, err := usherService.LiveChannels()
		if err != nil {
			fmt.Println("ERROR: Failed to get live channels from Usher:", err)
			return
		}
		usherSpectreChannels, err := usherService.SpectreChannels()
		if err != nil {
			fmt.Println("ERROR: Failed to get active spectre channels from Usher:", err)
			return
		}
		spectreChannels, _ := db.AllChannels()
		channels := mergeWithUsherData(spectreChannels, liveChannels, usherSpectreChannels)
		syncActive(channels)
	}
}

func mergeWithUsherData(spectreChannels []backend.Channel, liveChannels map[int]bool, usherSpectreChannels map[int]bool) []*Channel {
	channels := make([]*Channel, len(spectreChannels))
	for i, c := range spectreChannels {
		channels[i] = &Channel{
			Channel:       c,
			Live:          liveChannels[c.ID],
			ActiveInUsher: usherSpectreChannels[c.ID],
		}
	}
	fmt.Printf("This host is the master. Fetched channels from SpectreDB and Usher; "+
		"spectreChannels=%v, liveChannels=%v, activeInUsher=%v\n",
		len(spectreChannels), len(liveChannels), len(usherSpectreChannels))
	return channels
}
