package monitorsched

import (
	"context"
	"log"
	"sync"
	"time"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/checker"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/services"
)

const reloadInterval = 30 * time.Second

type runner struct {
	monitorID   string
	intervalSec int
	cancel      context.CancelFunc
}

func Start(ctx context.Context) {
	go loop(ctx)
}

func loop(ctx context.Context) {
	active := map[string]*runner{}
	var mu sync.Mutex

	// Monitors run regardless of licence state, deliberately.
	//
	// A customer whose card failed must not lose the ability to know their
	// infrastructure is on fire. Creating and editing monitors is blocked by the
	// API gate; executing the ones that already exist is not.
	sync := func() {
		monitors, err := services.ListServerScheduledMonitors()
		if err != nil {
			log.Printf("monitorsched: list monitors: %v", err)
			return
		}
		want := map[string]models.Monitor{}
		for _, m := range monitors {
			want[m.MonitorID] = m
		}

		mu.Lock()
		defer mu.Unlock()

		for id, r := range active {
			m, ok := want[id]
			if !ok || m.IntervalSec != r.intervalSec {
				r.cancel()
				delete(active, id)
			}
		}

		for id, m := range want {
			if _, ok := active[id]; ok {
				continue
			}
			rctx, cancel := context.WithCancel(ctx)
			active[id] = &runner{monitorID: id, intervalSec: m.IntervalSec, cancel: cancel}
			go runMonitor(rctx, m)
		}
	}

	sync()
	t := time.NewTicker(reloadInterval)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-t.C:
			sync()
		}
	}
}

func runMonitor(ctx context.Context, m models.Monitor) {
	interval := time.Duration(m.IntervalSec) * time.Second
	if interval <= 0 {
		interval = 60 * time.Second
	}
	spec := services.SpecFor(&m)

	run := func() {
		res := checker.Run(ctx, spec)
		if err := services.IngestServerScheduledResult(m.MonitorID, res); err != nil {
			log.Printf("monitorsched: ingest %s: %v", m.MonitorID, err)
		}
	}

	run()
	t := time.NewTicker(interval)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-t.C:
			run()
		}
	}
}
