package httpapi

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

// The cursor carries both created_at and entity_id because executions are
// ordered by the pair. created_at alone is not unique — several executions can
// share a millisecond — and paginating on a non-unique key silently drops or
// repeats rows at page boundaries. The previous cursor was the target slug,
// which is far less unique still: every execution against the same host shares
// it.
func TestExecutionCursorRoundTrips(t *testing.T) {
	created := time.Date(2026, 7, 28, 9, 15, 30, 123456789, time.UTC)
	id := uuid.MustParse("018f3a2b-0000-7000-8000-000000000042")

	cursor := formatExecutionCursor(created, id)

	gotTime, gotID, err := parseExecutionCursor(&cursor)
	if err != nil {
		t.Fatalf("parse: %v", err)
	}
	if !gotTime.Equal(created) {
		t.Errorf("time round-trip: got %v, want %v", gotTime, created)
	}
	if *gotID != id {
		t.Errorf("id round-trip: got %v, want %v", *gotID, id)
	}
}

func TestExecutionCursorNanosecondsSurvive(t *testing.T) {
	// Truncating to seconds would make the cursor ambiguous for executions
	// started in the same second, which is the normal case for a plan whose
	// steps run back to back.
	a := time.Date(2026, 7, 28, 9, 15, 30, 1, time.UTC)
	b := time.Date(2026, 7, 28, 9, 15, 30, 2, time.UTC)
	id := uuid.New()

	if formatExecutionCursor(a, id) == formatExecutionCursor(b, id) {
		t.Error("cursors one nanosecond apart must not collide")
	}
}

func TestExecutionCursorRejectsGarbage(t *testing.T) {
	empty := ""
	tm, id, err := parseExecutionCursor(&empty)
	if err != nil || tm != nil || id != nil {
		t.Errorf("empty cursor should mean 'no cursor', got %v/%v/%v", tm, id, err)
	}

	if tm, id, err := parseExecutionCursor(nil); err != nil || tm != nil || id != nil {
		t.Errorf("nil cursor should mean 'no cursor', got %v/%v/%v", tm, id, err)
	}

	for _, bad := range []string{"nonsense", "2026-07-28T09:15:30Z", "notatime,018f3a2b-0000-7000-8000-000000000042", "2026-07-28T09:15:30Z,notauuid"} {
		b := bad
		if _, _, err := parseExecutionCursor(&b); err == nil {
			t.Errorf("cursor %q should have been rejected", bad)
		}
	}
}
