package monitor

import (
	"database/sql"
	"fmt"
	"io"
	"log"
	"path/filepath"
	"testing"
	"time"

	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
	_ "modernc.org/sqlite"
)

// backdate sets a host's last_report_at to N minutes ago, simulating the passage
// of time without sleeping. Uses a second connection (the checker reads via store).
func backdate(t *testing.T, db *sql.DB, hostID string, minutesAgo int) {
	t.Helper()
	if _, err := db.Exec(`UPDATE hosts SET last_report_at = datetime('now', ?) WHERE host_id = ?`,
		fmt.Sprintf("-%d minutes", minutesAgo), hostID); err != nil {
		t.Fatal(err)
	}
}

func TestHostStalenessChecker(t *testing.T) {
	path := filepath.Join(t.TempDir(), "test.db")
	st, err := store.New(path, log.New(io.Discard, "", 0))
	if err != nil {
		t.Fatal(err)
	}
	defer st.Close()
	db, _ := sql.Open("sqlite", path)
	defer db.Close()

	st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ck", RetrievalPassword: "p"})
	st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "k1"})
	st.SaveHostReport("h1", "c1", []byte(`{}`), store.HostReportDenorm{}) // sets last_report_at

	var events []string
	onEvent := func(customerID, eventType, severity, message, detailsJSON, source string) {
		events = append(events, eventType)
	}

	// F2: already-stale (40m) at construction → left UNSEEDED (not seeded "stale"), NO event on init.
	backdate(t, db, "h1", 40)
	sc := NewHostStalenessChecker(st, 30*time.Minute, onEvent, log.New(io.Discard, "", 0))
	if len(events) != 0 {
		t.Fatalf("construction must not emit, got %v", events)
	}
	if sc.GetState("h1") != "unknown" {
		t.Fatalf("born-stale must be left unseeded, GetState = %q want unknown", sc.GetState("h1"))
	}

	// F2: the FIRST Check on a born-stale host emits host_stale once (persistent-down alerting).
	sc.Check()
	if len(events) != 1 || last(events) != "host_stale" {
		t.Fatalf("born-stale first Check → one host_stale, got %v", events)
	}
	// Steady stale → no duplicate.
	sc.Check()
	if len(events) != 1 {
		t.Fatalf("steady stale must not re-emit, got %v", events)
	}

	// Fresh report → host_recovered.
	backdate(t, db, "h1", 2)
	sc.Check()
	if last(events) != "host_recovered" {
		t.Fatalf("events = %v, want last host_recovered", events)
	}

	// Aged to stale → host_stale.
	backdate(t, db, "h1", 40)
	sc.Check()
	if last(events) != "host_stale" {
		t.Fatalf("events = %v, want last host_stale", events)
	}

	// Aged past 2× → host_down.
	backdate(t, db, "h1", 130)
	sc.Check()
	if last(events) != "host_down" {
		t.Fatalf("events = %v, want last host_down", events)
	}
}

func last(s []string) string {
	if len(s) == 0 {
		return ""
	}
	return s[len(s)-1]
}
