package audit

import (
	"log/slog"
	"path/filepath"
	"testing"
	"time"
)

// TestNopRecorder covers the disabled-auditing no-op implementation.
func TestNopRecorder(t *testing.T) {
	rec := Nop()
	rec.Record(Event{Action: "x"}) // must not panic
	got, err := rec.Query(Filter{})
	if err != nil {
		t.Fatalf("Nop Query: %v", err)
	}
	if len(got) != 0 {
		t.Errorf("Nop Query returned %d events, want 0", len(got))
	}
	if err := rec.Close(); err != nil {
		t.Errorf("Nop Close: %v", err)
	}
}

// TestQueryFilters covers the End, DS, and Signal (LIKE) filter branches plus
// the default-outcome and zero-time stamping in Record.
func TestQueryFilters(t *testing.T) {
	path := filepath.Join(t.TempDir(), "audit.db")
	rec, err := NewSQLite(path, slog.Default())
	if err != nil {
		t.Fatal("NewSQLite:", err)
	}
	defer rec.Close()

	// Anchor base in the past so the zero-Time 'c' record (stamped with now)
	// sorts after a/b and stays out of the End window below.
	base := time.Now().Add(-time.Hour)
	rec.Record(Event{Time: base, Actor: "a", ActorType: ActorUser, Action: "signal.write", DS: "epics", Signal: "PV:TEMP"})
	rec.Record(Event{Time: base.Add(time.Second), Actor: "b", ActorType: ActorUser, Action: "signal.write", DS: "synthetic", Signal: "PV:FLOW"})
	// Zero Time and empty Outcome exercise the defaulting branches in Record.
	rec.Record(Event{Actor: "c", ActorType: ActorUser, Action: "interface.update"})

	if err := rec.Close(); err != nil {
		t.Fatal("Close:", err)
	}
	rec, err = NewSQLite(path, slog.Default())
	if err != nil {
		t.Fatal("reopen:", err)
	}
	defer rec.Close()

	// End filter: only events at or before base.
	upTo, err := rec.Query(Filter{End: base.Add(500 * time.Millisecond)})
	if err != nil {
		t.Fatal("Query end:", err)
	}
	if len(upTo) != 1 || upTo[0].Actor != "a" {
		t.Errorf("end filter = %+v, want one 'a' event", upTo)
	}

	// DS filter.
	byDS, err := rec.Query(Filter{DS: "synthetic"})
	if err != nil {
		t.Fatal("Query ds:", err)
	}
	if len(byDS) != 1 || byDS[0].Signal != "PV:FLOW" {
		t.Errorf("ds filter = %+v, want one PV:FLOW event", byDS)
	}

	// Signal LIKE filter (substring match).
	bySignal, err := rec.Query(Filter{Signal: "TEMP"})
	if err != nil {
		t.Fatal("Query signal:", err)
	}
	if len(bySignal) != 1 || bySignal[0].DS != "epics" {
		t.Errorf("signal filter = %+v, want one epics event", bySignal)
	}

	// The zero-time/empty-outcome record was persisted with a default outcome.
	def, err := rec.Query(Filter{Actor: "c"})
	if err != nil {
		t.Fatal("Query actor c:", err)
	}
	if len(def) != 1 || def[0].Outcome != OutcomeOK {
		t.Errorf("defaulted record = %+v, want one event with outcome ok", def)
	}
	if def[0].Time.IsZero() {
		t.Error("zero Time should have been stamped with now")
	}
}
