package tlspool

import (
	"reflect"
	"testing"
)

func TestBankGrowsToSize(t *testing.T) {
	p := New(3, 100)
	seen := map[uintptr]bool{}
	for i := 0; i < 9; i++ {
		h := p.Get("")
		if h == nil {
			t.Fatal("session is nil")
		}
		seen[sessionKey(h)] = true
	}
	if got := p.Size(""); got != 3 {
		t.Fatalf("bank size = %d, want 3", got)
	}
	if len(seen) != 3 {
		t.Fatalf("distinct sessions = %d, want 3 (round-robin reuse)", len(seen))
	}
}

func TestSessionRotatesAfterMaxUses(t *testing.T) {
	p := New(1, 5)
	first := sessionKey(p.Get(""))
	for i := 0; i < 4; i++ {
		if sessionKey(p.Get("")) != first {
			t.Fatalf("session rotated before its budget was spent (call %d)", i+2)
		}
	}
	// 6th call exceeds 5 uses: must be a fresh session.
	if got := sessionKey(p.Get("")); got == first {
		t.Fatal("session must be replaced after maxUses")
	}
}

func TestResetDropsBank(t *testing.T) {
	p := New(2, 100)
	p.Get("")
	p.Get("")
	if p.Size("") != 2 {
		t.Fatalf("bank size = %d before reset, want 2", p.Size(""))
	}
	p.Reset("")
	if p.Size("") != 0 {
		t.Fatalf("bank size = %d after reset, want 0", p.Size(""))
	}
}

func TestDefaults(t *testing.T) {
	p := New(0, 0)
	if p.size != 12 || p.maxUses != 50 {
		t.Fatalf("defaults = %d/%d, want 12/50", p.size, p.maxUses)
	}
}

func TestTakeWarmsEachSessionOnce(t *testing.T) {
	p := New(1, 100)
	_, warm := p.Take("")
	if !warm {
		t.Fatal("first take of a fresh session must report needsWarm")
	}
	for i := 0; i < 5; i++ {
		if _, warm := p.Take(""); warm {
			t.Fatalf("take %d of the same session must not report needsWarm", i+2)
		}
	}
	// Reset rebuilds the bank: the replacement session needs warmup again.
	p.Reset("")
	if _, warm := p.Take(""); !warm {
		t.Fatal("session after Reset must report needsWarm")
	}
}

func TestTakeWarmsAfterRotation(t *testing.T) {
	p := New(1, 2)
	first, _ := p.Take("")
	p.Take("") // 2nd use: same session
	h, warm := p.Take("")
	if sessionKey(h) == sessionKey(first) {
		t.Fatal("session must be replaced after maxUses")
	}
	if !warm {
		t.Fatal("rotated session must report needsWarm")
	}
}

func sessionKey(h interface{}) uintptr {
	if h == nil {
		return 0
	}
	return reflect.ValueOf(h).Pointer()
}
