package mtproto

import (
	"context"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"

	"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
	"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
)

func TestMsgSeq(t *testing.T) {
	type record struct {
		msgID int64
		seqNo int32
	}

	var (
		records []record
		mux     sync.Mutex
	)

	const (
		workers           = 32
		requestsPerWorker = 200
	)
	client := newTestClient(func(msgID int64, seqNo int32, body bin.Encoder) (bin.Encoder, error) {
		mux.Lock()
		records = append(records, record{msgID, seqNo})
		mux.Unlock()
		return &tg.Config{}, nil
	})

	{
		var wg sync.WaitGroup
		wg.Add(workers)
		for i := 0; i < workers; i++ {
			go func(t *testing.T) {
				defer wg.Done()

				for i := 0; i < requestsPerWorker; i++ {
					err := client.Invoke(context.Background(), &tg.Config{}, &tg.Config{})
					require.NoError(t, err)
				}
			}(t)
		}

		wg.Wait()
	}

	var received []record
	less := func(msgID int64, f func(r record)) {
		for _, recv := range received {
			if recv.msgID < msgID {
				f(recv)
			}
		}
	}

	greater := func(msgID int64, f func(r record)) {
		for _, recv := range received {
			if recv.msgID > msgID {
				f(recv)
			}
		}
	}

	contains := func(msgID int64) bool {
		for _, rec := range received {
			if rec.msgID == msgID {
				return true
			}
		}

		return false
	}

	for _, current := range records {
		if contains(current.msgID) {
			// Ignore duplicates.
			continue
		}

		less(current.msgID, func(less record) {
			// The server has already received a message with a lower msg_id
			// but with either a higher or an equal and odd seqno.
			if less.seqNo >= current.seqNo && less.seqNo%2 == 1 {
				t.Fatal("seqNo too low")
			}
		})

		greater(current.msgID, func(greater record) {
			// Similarly, there is a message with a higher msg_id
			// but with either a lower or an equal and odd seqno.
			if greater.seqNo <= current.seqNo && greater.seqNo%2 == 1 {
				t.Fatal("seqNo too high")
			}
		})

		received = append(received, current)
	}
}
