package uisession

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

// TestTombstoneRejectionAlignsWithCurrentAPISignatures exercises the
// per-API rejection contract enumerated in ruling #19: only Select and
// Subscribe return typed ErrSessionClosed; the others use their existing
// bool/ok returns. A second Revoke with a later expiresAt MUST NOT extend
// the tombstone upper bound.
func TestTombstoneRejectionAlignsWithCurrentAPISignatures(t *testing.T) {
	now := time.Date(2026, 7, 16, 0, 0, 0, 0, time.UTC)
	store := NewStore(Options{Now: func() time.Time { return now }})
	expiresAt := now.Add(time.Hour)

	const sessionID = "session-revoked"
	const serverID = "server-1"

	_, err := store.Select(sessionID, expiresAt, serverID)
	require.NoError(t, err)

	store.Revoke(sessionID, expiresAt)

	// Select and Subscribe return typed ErrSessionClosed.
	_, err = store.Select(sessionID, expiresAt, serverID)
	require.ErrorIs(t, err, ErrSessionClosed)
	_, err = store.Subscribe(sessionID, expiresAt)
	require.ErrorIs(t, err, ErrSessionClosed)

	// Clear / ClearIfGeneration / WatchGeneration use their existing
	// (ok=false / false / (nil, noopRelease, false)) returns.
	_, ok := store.Clear(sessionID)
	require.False(t, ok)
	require.False(t, store.ClearIfGeneration(sessionID, 1))

	ch, release, watchOK := store.WatchGeneration(sessionID, 1)
	require.False(t, watchOK)
	require.Nil(t, ch)
	if release != nil {
		release()
	}

	// Server-scoped APIs do NOT typed-error per session; they skip the
	// tombstoned entry because Revoke already deleted it. They MUST NOT
	// resurrect the entry.
	require.Equal(t, 0, store.AdvanceServerGeneration(serverID))
	transitions := store.BeginServerTransitionHandle(serverID)
	transitions.Complete()

	// entries[sessionID] remains absent.
	_, exists := store.Get(sessionID)
	require.False(t, exists)

	// A second Revoke with a later expiresAt MUST NOT extend the tombstone.
	firstRecorded, hasTombstone := tombstoneExpiresAtLocked(store, sessionID)
	require.True(t, hasTombstone)
	require.Equal(t, expiresAt, firstRecorded)

	later := expiresAt.Add(time.Hour)
	store.Revoke(sessionID, later)
	secondRecorded, _ := tombstoneExpiresAtLocked(store, sessionID)
	require.Equal(t, firstRecorded, secondRecorded, "later Revoke MUST NOT extend tombstone")
}

// TestTombstoneRejectionAdvancesAndTransitionsNeverResurrect covers the
// server-scoped paths: a tombstoned session cannot be re-created by
// AdvanceServerGeneration or BeginServerTransition. This complements
// TestTombstoneRejectionAlignsWithCurrentAPISignatures.
func TestTombstoneRejectionAdvancesAndTransitionsNeverResurrect(t *testing.T) {
	now := time.Date(2026, 7, 16, 0, 0, 0, 0, time.UTC)
	store := NewStore(Options{Now: func() time.Time { return now }})
	expiresAt := now.Add(time.Hour)

	const sessionID = "session-advance-after-revoke"
	const serverID = "server-2"

	_, err := store.Select(sessionID, expiresAt, serverID)
	require.NoError(t, err)
	store.Revoke(sessionID, expiresAt)

	require.Equal(t, 0, store.AdvanceServerGeneration(serverID))

	_, exists := store.Get(sessionID)
	require.False(t, exists)
}
