package imapserver_test

import (
	"testing"

	"github.com/emersion/go-imap/v2/imapserver"
)

type trackerUpdate struct {
	expunge     uint32
	numMessages uint32
}

var sessionTrackerSeqNumTests = []struct {
	name                       string
	pending                    []trackerUpdate
	clientSeqNum, serverSeqNum uint32
}{
	{
		name:         "noop",
		pending:      nil,
		clientSeqNum: 20,
		serverSeqNum: 20,
	},
	{
		name:         "noop_last",
		pending:      nil,
		clientSeqNum: 42,
		serverSeqNum: 42,
	},
	{
		name:         "noop_client_oob",
		pending:      nil,
		clientSeqNum: 43,
		serverSeqNum: 0,
	},
	{
		name:         "noop_server_oob",
		pending:      nil,
		clientSeqNum: 0,
		serverSeqNum: 43,
	},
	{
		name:         "expunge_eq",
		pending:      []trackerUpdate{{expunge: 20}},
		clientSeqNum: 20,
		serverSeqNum: 0,
	},
	{
		name:         "expunge_lt",
		pending:      []trackerUpdate{{expunge: 20}},
		clientSeqNum: 10,
		serverSeqNum: 10,
	},
	{
		name:         "expunge_gt",
		pending:      []trackerUpdate{{expunge: 10}},
		clientSeqNum: 20,
		serverSeqNum: 19,
	},
	{
		name:         "append_eq",
		pending:      []trackerUpdate{{numMessages: 43}},
		clientSeqNum: 0,
		serverSeqNum: 43,
	},
	{
		name:         "append_lt",
		pending:      []trackerUpdate{{numMessages: 43}},
		clientSeqNum: 42,
		serverSeqNum: 42,
	},
	{
		name: "expunge_append",
		pending: []trackerUpdate{
			{expunge: 42},
			{numMessages: 42},
		},
		clientSeqNum: 42,
		serverSeqNum: 0,
	},
	{
		name: "expunge_append",
		pending: []trackerUpdate{
			{expunge: 42},
			{numMessages: 42},
		},
		clientSeqNum: 0,
		serverSeqNum: 42,
	},
	{
		name: "append_expunge",
		pending: []trackerUpdate{
			{numMessages: 43},
			{expunge: 42},
		},
		clientSeqNum: 42,
		serverSeqNum: 0,
	},
	{
		name: "append_expunge",
		pending: []trackerUpdate{
			{numMessages: 43},
			{expunge: 42},
		},
		clientSeqNum: 0,
		serverSeqNum: 42,
	},
	{
		name: "multi_expunge_middle",
		pending: []trackerUpdate{
			{expunge: 3},
			{expunge: 1},
		},
		clientSeqNum: 2,
		serverSeqNum: 1,
	},
	{
		name: "multi_expunge_after",
		pending: []trackerUpdate{
			{expunge: 3},
			{expunge: 1},
		},
		clientSeqNum: 4,
		serverSeqNum: 2,
	},
}

func TestSessionTracker(t *testing.T) {
	for _, tc := range sessionTrackerSeqNumTests {
		tc := tc // capture range variable
		t.Run(tc.name, func(t *testing.T) {
			mboxTracker := imapserver.NewMailboxTracker(42)
			sessTracker := mboxTracker.NewSession()
			for _, update := range tc.pending {
				switch {
				case update.expunge != 0:
					mboxTracker.QueueExpunge(update.expunge)
				case update.numMessages != 0:
					mboxTracker.QueueNumMessages(update.numMessages)
				}
			}

			serverSeqNum := sessTracker.DecodeSeqNum(tc.clientSeqNum)
			if tc.clientSeqNum != 0 && serverSeqNum != tc.serverSeqNum {
				t.Errorf("DecodeSeqNum(%v): got %v, want %v", tc.clientSeqNum, serverSeqNum, tc.serverSeqNum)
			}

			clientSeqNum := sessTracker.EncodeSeqNum(tc.serverSeqNum)
			if tc.serverSeqNum != 0 && clientSeqNum != tc.clientSeqNum {
				t.Errorf("EncodeSeqNum(%v): got %v, want %v", tc.serverSeqNum, clientSeqNum, tc.clientSeqNum)
			}
		})
	}
}
