package main

import (
	"encoding/json"
	"math"
	"os"
	"sync"
	"sync/atomic"
	"testing"
)

func sample() Workload { return Workload{1_000_000, 512, 200, 200, 200, 50, 5, 150, 30} }
func TestAnalyze(t *testing.T) {
	got, err := Analyze(sample())
	if err != nil || got.MeanInFlight != 40 || got.GiBSeconds != 100_000 || got.Connections != 250 || got.Budget != 120 || got.FitInstances != 24 {
		t.Fatal(got, err)
	}
	w := sample()
	w.MemoryMiB = 0
	if _, err = Analyze(w); err == nil {
		t.Fatal("accepted zero memory")
	}
	w = sample()
	w.Requests = math.MaxInt64
	if _, err = Analyze(w); err == nil {
		t.Fatal("accepted overflow")
	}
	w = sample()
	w.DBReserve = w.DBMax
	if _, err = Analyze(w); err == nil {
		t.Fatal("accepted no DB budget")
	}
}
func TestConcurrentRedelivery(t *testing.T) {
	l := NewLedger(10)
	var applied atomic.Int64
	var wg sync.WaitGroup
	for range 100 {
		wg.Go(func() {
			ok, err := l.Reserve(Event{"x", 2}, false)
			if err != nil {
				t.Error(err)
			}
			if ok {
				applied.Add(1)
			}
		})
	}
	wg.Wait()
	if applied.Load() != 1 || l.Stock() != 8 {
		t.Fatal(applied.Load(), l.Stock())
	}
	if _, err := l.Reserve(Event{"x", 3}, false); err == nil {
		t.Fatal("payload conflict accepted")
	}
}
func TestRetryBeforeCommit(t *testing.T) {
	l := NewLedger(10)
	if _, err := l.Reserve(Event{"x", 2}, true); err == nil {
		t.Fatal("injection missing")
	}
	if ok, err := l.Reserve(Event{"x", 2}, false); !ok || err != nil || l.Stock() != 8 {
		t.Fatal(ok, err, l.Stock())
	}
}
func TestBadMarkFirst(t *testing.T) {
	stock := 10
	done := make(map[string]bool)
	e := Event{"x", 2}
	badMarkFirst(&stock, done, e, true)
	if badMarkFirst(&stock, done, e, false) || stock != 10 {
		t.Fatal("negative fixture changed")
	}
	// The test passes because it detects the deliberately lost reservation.
	if !done[e.ID] {
		t.Fatal("missing bad marker")
	}
}
func TestInvalidEvent(t *testing.T) {
	for _, e := range []Event{{"", 1}, {"x", 0}, {"x", -1}, {"x", 11}} {
		l := NewLedger(10)
		if _, err := l.Reserve(e, false); err == nil || l.Stock() != 10 {
			t.Fatal(e, err)
		}
	}
}
func TestJSONInputs(t *testing.T) {
	for _, data := range []string{`{}`, `{"unexpected":1}`, `{} {}`, `{`} {
		p := t.TempDir() + "/w.json"
		if err := os.WriteFile(p, []byte(data), 0600); err != nil {
			t.Fatal(err)
		}
		w, err := readWorkload(p)
		if err == nil {
			_, err = Analyze(w)
		}
		if err == nil {
			t.Fatalf("accepted %s", data)
		}
	}
}
func TestPolicyIsOnlyJSONFixture(t *testing.T) {
	b, err := os.ReadFile("iam-read-example.json")
	if err != nil {
		t.Fatal(err)
	}
	var v map[string]any
	if err = json.Unmarshal(b, &v); err != nil {
		t.Fatal(err)
	}
	// Syntax check only. This does not evaluate AWS IAM effective permissions.
	if v["Version"] != "2012-10-17" {
		t.Fatal(v)
	}
}
