package main

import (
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func TestConcurrentExactlyOneLocalClaim(t *testing.T) {
	s := NewStore()
	k := Key{"a", "add", "1"}
	var claims atomic.Int64
	var wg sync.WaitGroup
	for range 100 {
		wg.Go(func() {
			e, status := s.Start(k, []byte("1"), time.Time{})
			if status == "claimed" {
				claims.Add(1)
				if _, err := s.Commit(k, e.Generation, 1, time.Time{}, time.Hour); err != nil {
					t.Error(err)
				}
			}
		})
	}
	wg.Wait()
	if claims.Load() != 1 || s.Balance() != 1 {
		t.Fatal(claims.Load(), s.Balance())
	}
}
func TestPendingAndUnknownNeverSilentlyExpire(t *testing.T) {
	s := NewStore()
	k := Key{"a", "add", "1"}
	e, _ := s.Start(k, nil, time.Time{})
	if _, status := s.Start(k, nil, time.Time{}.Add(100*time.Hour)); status != "pending" {
		t.Fatal(status)
	}
	if err := s.MarkUnknown(k, e.Generation); err != nil {
		t.Fatal(err)
	}
	if _, status := s.Start(k, nil, time.Time{}.Add(100*time.Hour)); status != "unknown" {
		t.Fatal(status)
	}
	if _, err := s.Commit(k, e.Generation, 1, time.Time{}, time.Hour); err == nil {
		t.Fatal("unknown cannot blindly commit")
	}
}
func TestConflictRetentionAndStaleOwner(t *testing.T) {
	s := NewStore()
	k := Key{"a", "add", "1"}
	now := time.Time{}
	e, _ := s.Start(k, []byte("1"), now)
	if _, status := s.Start(k, []byte("2"), now); status != "conflict" {
		t.Fatal(status)
	}
	if _, err := s.Commit(k, e.Generation, 1, now, time.Hour); err != nil {
		t.Fatal(err)
	}
	if _, status := s.Start(k, []byte("1"), now.Add(time.Hour-time.Nanosecond)); status != "replay" {
		t.Fatal(status)
	}
	next, status := s.Start(k, []byte("1"), now.Add(time.Hour))
	if status != "claimed" {
		t.Fatal(status)
	}
	if _, err := s.Commit(k, e.Generation, 10, now, time.Hour); err == nil {
		t.Fatal("stale generation accepted")
	}
	if _, err := s.Commit(k, next.Generation, 1, now, time.Hour); err != nil {
		t.Fatal(err)
	}
	if s.Balance() != 2 {
		t.Fatal(s.Balance())
	}
}
func TestTenantScope(t *testing.T) {
	s := NewStore()
	for _, tenant := range []string{"a", "b"} {
		if _, status := s.Start(Key{tenant, "add", "1"}, nil, time.Time{}); status != "claimed" {
			t.Fatal(status)
		}
	}
}
