package redriver

import (
	"log"
	"strconv"
	"strings"
	"testing"
	"time"
)

func TestMessageStore_TTL(t *testing.T) {
	const (
		rh  = "foo"
		ttl = 1 * time.Millisecond
	)
	m1 := Message{ReceiptHandle: rh}
	actual := newMessageStore(ttl)

	_, ok := actual.Get(rh)
	if ok {
		t.Errorf("message found, but not yet stored")
	}

	actual.Set(m1)
	m2, ok := actual.Get(rh)
	if !ok {
		t.Errorf("message not found, but expected to find it")
	}
	if m2.ReceiptHandle != rh {
		t.Errorf("message RH is %q but key is %q", m2.ReceiptHandle, rh)
	}
	time.Sleep(3 * ttl)
	_, ok = actual.Get(rh)
	if ok {
		t.Errorf("message found, but expected it to be expired")
	}
}

func TestMessageStore_Flush(t *testing.T) {
	len := func(ms *messageStore) int {
		count := 0
		ms.Range(func(_, _ any) bool {
			count++
			return true
		})
		return count
	}
	const limit = 10
	ms := newMessageStore(1 * time.Minute)
	defer ms.Close()
	for i := 0; i < limit; i++ {
		ms.Set(Message{ReceiptHandle: strconv.Itoa(i)})
	}
	if actualLen := len(ms); actualLen != limit {
		t.Fatalf("expected %d items, got %d", len(ms), actualLen)
	}
	ms.Flush()
	if actualLen := len(ms); actualLen != 0 {
		t.Fatalf("expected no items, got %d", actualLen)
	}

}

func TestMessageStore_Set(t *testing.T) {
	for _, test := range [...]struct {
		name      string
		input     string
		expectErr bool
	}{
		{"valid RH", "foo", false},
		{"empty RH", "", true},
	} {
		t.Run(test.name, func(t *testing.T) {
			ms := newMessageStore(time.Minute)
			defer ms.Close()
			actual := ms.Set(Message{ReceiptHandle: test.input})
			if actual != nil != test.expectErr {
				t.Errorf("expected error %t, but got error %t", test.expectErr, actual)
			}
		})
	}
}

func TestMessageStore_Get_sad(t *testing.T) {
	ms := newMessageStore(time.Minute)
	defer ms.Close()
	ms.Map.Store("foo", "sad")
	_, ok := ms.Get("foo")
	if ok {
		t.Fatalf("expected failure, but got success")
	}
}

func TestMessageStore_expire(t *testing.T) {
	const ttl = 1 * time.Millisecond
	ms := newMessageStore(ttl)
	w := &strings.Builder{}
	log.SetOutput(w)
	ms.Map.Store(13, Message{ReceiptHandle: "13"})
	ms.Map.Store("13", "vendredi")
	time.Sleep(3 * ttl)
	ms.Close()
	s := w.String()
	if !strings.Contains(s, "map contains a non-string key") {
		t.Errorf("expected expiry to have logged a key warning, but found none")
	}
	if !strings.Contains(s, "map contains a non-Messsage entry") {
		t.Errorf("expected expiry to have logged a data warning, but found none")
	}

}
