//go:build !nautilus
// +build !nautilus

package rbd

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestLocking(t *testing.T) {
	conn := radosConnect(t)
	require.NotNil(t, conn)
	defer conn.Shutdown()

	poolname := GetUUID()
	err := conn.MakePool(poolname)
	require.NoError(t, err)
	defer conn.DeletePool(poolname)

	ioctx, err := conn.OpenIOContext(poolname)
	require.NoError(t, err)
	defer ioctx.Destroy()

	// Connect another cluster
	conn1 := radosConnect(t)
	require.NotNil(t, conn1)
	defer conn1.Shutdown()

	ioctx1, err := conn1.OpenIOContext(poolname)
	require.NoError(t, err)
	defer ioctx1.Destroy()

	options := NewRbdImageOptions()
	defer options.Destroy()
	assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))

	name := GetUUID()
	err = CreateImage(ioctx, name, testImageSize, options)
	assert.NoError(t, err)

	t.Run("acquireLock", func(t *testing.T) {
		img, err := OpenImage(ioctx, name, NoSnapshot)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, img.Close())
		}()

		// Shared lock mode is not supported
		// Ref: Check lock_acquire() logic in ceph codebase at https://github.com/ceph/ceph/blob/main/src/librbd/internal.cc
		err = img.LockAcquire(LockModeShared)
		assert.Error(t, err)

		err = img.LockAcquire(LockModeExclusive)
		assert.NoError(t, err)

		isOwner, err := img.LockIsExclusiveOwner()
		assert.NoError(t, err)
		assert.True(t, isOwner)

		err = img.LockBreak(LockModeExclusive, "not owner")
		assert.Error(t, err)

		err = img.LockRelease()
		assert.NoError(t, err)
	})

	t.Run("listLock", func(t *testing.T) {
		img, err := OpenImage(ioctx, name, NoSnapshot)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, img.Close())
		}()

		img1, err := OpenImage(ioctx1, name, NoSnapshot)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, img1.Close())
		}()

		err = img1.LockAcquire(LockModeExclusive)
		assert.NoError(t, err)

		err = img.LockAcquire(LockModeExclusive)
		assert.Error(t, err)

		isOwner, err := img.LockIsExclusiveOwner()
		assert.NoError(t, err)
		assert.False(t, isOwner)

		// This logic of fetching the lock owners and breaking the lock using a
		// different image is borrowed from the TCs in ceph codebase, check
		// the TEST_F(TestLibRBD, BreakLock) at https://github.com/ceph/ceph/blob/main/src/test/librbd/test_librbd.cc
		locksList, err := img.LockGetOwners()
		assert.NoError(t, err)
		assert.Equal(t, len(locksList), 1)
		assert.Equal(t, locksList[0].Mode, LockModeExclusive)

		err = img.LockBreak(LockModeExclusive, locksList[0].Owner)
		assert.NoError(t, err)

		err = img.LockAcquire(LockModeExclusive)
		assert.NoError(t, err)

		err = img.LockRelease()
		assert.NoError(t, err)
	})

	t.Run("emptyListLock", func(t *testing.T) {
		img, err := OpenImage(ioctx, name, NoSnapshot)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, img.Close())
		}()

		locksList, err := img.LockGetOwners()
		assert.NoError(t, err)
		assert.Equal(t, len(locksList), 0)
	})

	t.Run("closedImage", func(t *testing.T) {
		img, err := OpenImage(ioctx, name, NoSnapshot)
		assert.NoError(t, err)
		assert.NoError(t, img.Close())
		defer func() {
			assert.NoError(t, img.Remove())
		}()

		err = img.LockAcquire(LockModeExclusive)
		assert.Error(t, err)

		err = img.LockBreak(LockModeExclusive, "")
		assert.Error(t, err)

		_, err = img.LockGetOwners()
		assert.Error(t, err)

		_, err = img.LockIsExclusiveOwner()
		assert.Error(t, err)

		err = img.LockRelease()
		assert.Error(t, err)
	})
}
