// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package kmsg_test

import (
	"context"
	"errors"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/talos-systems/talos/internal/pkg/kmsg"
)

func skipIfNoKmsg(t *testing.T) {
	f, err := os.OpenFile("/dev/kmsg", os.O_RDONLY, 0)
	if err != nil {
		t.Skip("/dev/kmsg is not available", err.Error())
	}

	f.Close() //nolint:errcheck
}

func TestReaderNoFollow(t *testing.T) {
	skipIfNoKmsg(t)

	r, err := kmsg.NewReader()
	assert.NoError(t, err)

	defer r.Close() //nolint:errcheck

	messageCount := 0

	for packet := range r.Scan(context.Background()) {
		assert.NoError(t, packet.Err)

		messageCount++
	}

	assert.Greater(t, messageCount, 0)

	assert.NoError(t, r.Close())
}

func TestReaderFollow(t *testing.T) {
	testReaderFollow(t, true)
}

func TestReaderFollowTail(t *testing.T) {
	testReaderFollow(t, false, kmsg.FromTail())
}

func testReaderFollow(t *testing.T, expectMessages bool, options ...kmsg.Option) {
	skipIfNoKmsg(t)

	r, err := kmsg.NewReader(append([]kmsg.Option{kmsg.Follow()}, options...)...)
	assert.NoError(t, err)

	defer r.Close() //nolint:errcheck

	messageCount := 0

	ctx, ctxCancel := context.WithCancel(context.Background())
	defer ctxCancel()

	ch := r.Scan(ctx)

	var closed bool

LOOP:
	for {
		select {
		case packet, ok := <-ch:
			if !ok {
				if !closed {
					assert.Fail(t, "channel closed before cancel")
				}

				break LOOP
			}

			if closed && errors.Is(packet.Err, os.ErrClosed) {
				// ignore 'file already closed' error as it might happen
				// from the branch below depending on whether context cancel or
				// read() finishes first
				continue
			}

			assert.NoError(t, packet.Err)

			messageCount++
		case <-time.After(100 * time.Millisecond):
			// abort
			closed = true
			ctxCancel()
			assert.NoError(t, r.Close())
		}
	}

	if expectMessages {
		assert.Greater(t, messageCount, 0)
	}
}
