package observability

import (
	"context"
	"database/sql"
	"path/filepath"
	"testing"

	"github.com/rs/zerolog"
	_ "modernc.org/sqlite"
)

func openTestDB(t *testing.T) *sql.DB {
	t.Helper()
	dbPath := filepath.Join(t.TempDir(), "test.db")
	db, err := sql.Open("sqlite", dbPath)
	if err != nil {
		t.Fatalf("open sqlite: %v", err)
	}
	t.Cleanup(func() { _ = db.Close() })
	if _, err := db.Exec(`
		CREATE TABLE system_events (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			timestamp INTEGER NOT NULL,
			level TEXT NOT NULL,
			component TEXT NOT NULL,
			message TEXT NOT NULL,
			context_json TEXT,
			correlation_id TEXT
		);`); err != nil {
		t.Fatalf("create table: %v", err)
	}
	return db
}

func TestRecordAndQuery(t *testing.T) {
	db := openTestDB(t)
	rec := NewEventRecorder(db, zerolog.Nop())
	ctx := context.Background()

	rec.Record(ctx, LevelInfo, "boot", "started", map[string]any{"port": 8443}, "")
	rec.Record(ctx, LevelError, "scrape", "tracker timeout", map[string]any{"tracker": "x"}, "corr-1")
	rec.Record(ctx, LevelInfo, "scrape", "tracker ok", nil, "corr-1")

	all, err := rec.Query(ctx, "", "", 100)
	if err != nil {
		t.Fatalf("Query all: %v", err)
	}
	if len(all) != 3 {
		t.Fatalf("Query all: got %d events, want 3", len(all))
	}

	scrape, err := rec.Query(ctx, "", "scrape", 100)
	if err != nil {
		t.Fatalf("Query scrape: %v", err)
	}
	if len(scrape) != 2 {
		t.Fatalf("Query scrape: got %d, want 2", len(scrape))
	}

	errs, err := rec.Query(ctx, LevelError, "", 100)
	if err != nil {
		t.Fatalf("Query errors: %v", err)
	}
	if len(errs) != 1 {
		t.Fatalf("Query errors: got %d, want 1", len(errs))
	}
	if errs[0].CorrelationID != "corr-1" {
		t.Errorf("correlation_id not preserved: got %q", errs[0].CorrelationID)
	}
}

func TestRecordWithoutDB(t *testing.T) {
	rec := NewEventRecorder(nil, zerolog.Nop())
	// Must not panic when db is nil.
	rec.Record(context.Background(), LevelInfo, "boot", "ok", nil, "")
}

func TestHealthChecker(t *testing.T) {
	db := openTestDB(t)
	h := NewHealthChecker(db, nil, "/tmp/age.key", "test", false)
	got := h.Check(context.Background())
	if got.Status != StatusUnhealthy {
		t.Errorf("before config marked: %q, want unhealthy", got.Status)
	}
	h.MarkConfigLoaded()
	got = h.Check(context.Background())
	// DuckDB is now `disabled` (intentional build-time choice) instead of
	// `degraded` — sqlite + age_key + config are all ok, so the rolled-up
	// status should be ok. See StatusDisabled doc in health.go for the
	// reasoning. Operator-facing dashboards no longer flag a phantom alarm.
	if got.Status != StatusOK {
		t.Errorf("after config marked w/ duckdb disabled: %q, want ok", got.Status)
	}
	// Find the duckdb check + confirm it's now `disabled`.
	var duck *Check
	for i := range got.Checks {
		if got.Checks[i].Name == "duckdb" {
			duck = &got.Checks[i]
		}
	}
	if duck == nil || duck.Status != StatusDisabled {
		t.Errorf("duckdb check status: %v, want %q", duck, StatusDisabled)
	}
}
