package lock_manager

import (
	"fmt"
	"testing"
	"time"

	"github.com/seaweedfs/seaweedfs/weed/pb"
)

func TestLockRing_PriorOwner(t *testing.T) {
	r := NewLockRing(5 * time.Second)
	t.Cleanup(r.WaitForCleanup)

	setA := []pb.ServerAddress{"s1:1", "s2:1", "s3:1"}
	r.SetSnapshot(setA, 1)

	// Only one snapshot: nothing to fall back to.
	if got := r.PriorOwner("any"); got != "" {
		t.Fatalf("single snapshot should have no prior owner, got %q", got)
	}

	// Add a server so some keys' ownership moves.
	setB := []pb.ServerAddress{"s1:1", "s2:1", "s3:1", "s4:1"}
	r.SetSnapshot(setB, 2)

	var moved, stable string
	for i := 0; i < 2000 && (moved == "" || stable == ""); i++ {
		key := fmt.Sprintf("key-%d", i)
		if r.GetPrimary(key) != hashKeyToServer(key, setA) {
			if moved == "" {
				moved = key
			}
		} else if stable == "" {
			stable = key
		}
	}
	if moved == "" || stable == "" {
		t.Skip("could not find both a moved and a stable key")
	}

	if got, want := r.PriorOwner(moved), hashKeyToServer(moved, setA); got != want {
		t.Fatalf("PriorOwner(moved)=%q, want %q", got, want)
	}
	if got := r.PriorOwner(stable); got != "" {
		t.Fatalf("unmoved key should have no prior owner, got %q", got)
	}
}

func TestLockRing_PriorOwnerExpires(t *testing.T) {
	r := NewLockRing(20 * time.Millisecond)
	t.Cleanup(r.WaitForCleanup)
	r.SetSnapshot([]pb.ServerAddress{"s1:1", "s2:1", "s3:1"}, 1)
	r.SetSnapshot([]pb.ServerAddress{"s1:1", "s2:1", "s3:1", "s4:1"}, 2)

	// Past the cooling interval, the prior owner is no longer offered.
	time.Sleep(40 * time.Millisecond)
	for i := 0; i < 2000; i++ {
		if got := r.PriorOwner(fmt.Sprintf("key-%d", i)); got != "" {
			t.Fatalf("prior owner should expire after the cooling interval, got %q", got)
		}
	}
}

// WriteOwner resolves prior-else-current under one read lock, so the two views
// can never come from different rings.
func TestLockRing_WriteOwner(t *testing.T) {
	r := NewLockRing(5 * time.Second)
	t.Cleanup(r.WaitForCleanup)

	setA := []pb.ServerAddress{"s1:1", "s2:1", "s3:1"}
	r.SetSnapshot(setA, 1)

	// One snapshot: no prior owner, so the primary owns every key.
	if got, want := r.WriteOwner("any"), r.GetPrimary("any"); got != want {
		t.Fatalf("WriteOwner=%q, want the primary %q", got, want)
	}

	r.SetSnapshot([]pb.ServerAddress{"s1:1", "s2:1", "s3:1", "s4:1"}, 2)

	var moved, stable string
	for i := 0; i < 2000 && (moved == "" || stable == ""); i++ {
		key := fmt.Sprintf("key-%d", i)
		if r.PriorOwner(key) != "" {
			if moved == "" {
				moved = key
			}
		} else if stable == "" {
			stable = key
		}
	}
	if moved == "" || stable == "" {
		t.Skip("could not find both a moved and a stable key")
	}

	if got, want := r.WriteOwner(moved), r.PriorOwner(moved); got != want {
		t.Fatalf("moved key: WriteOwner=%q, want the prior owner %q", got, want)
	}
	if got, want := r.WriteOwner(stable), r.GetPrimary(stable); got != want {
		t.Fatalf("stable key: WriteOwner=%q, want the primary %q", got, want)
	}
}
