package rbd

import (
	"testing"

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

func TestGetSnapNamespaceType(t *testing.T) {
	conn := radosConnect(t)
	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()

	imageName := "parent"
	snapName := "mySnap"
	options := NewRbdImageOptions()
	defer options.Destroy()
	err = options.SetUint64(ImageOptionOrder, uint64(testImageOrder))
	assert.NoError(t, err)
	err = options.SetUint64(ImageOptionFeatures, 1)
	assert.NoError(t, err)

	err = CreateImage(ioctx, imageName, testImageSize, options)
	assert.NoError(t, err)

	img, err := OpenImage(ioctx, imageName, NoSnapshot)
	assert.NoError(t, err)

	defer func() {
		assert.NoError(t, img.Close())
		assert.NoError(t, img.Remove())
	}()

	snapshot, err := img.CreateSnapshot(snapName)
	assert.NoError(t, err)

	snapInfoList, err := img.GetSnapshotNames()
	assert.NoError(t, err)

	snapInfo := snapInfoList[0]
	assert.Equal(t, snapInfo.Name, snapName)

	t.Run("SnapNamespaceTypeInvalidArgs", func(t *testing.T) {
		validImageName := GetUUID()
		validImg, err := Create(ioctx, validImageName, testImageSize, testImageOrder, 1)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, validImg.Remove())
		}()

		validImg = GetImage(ioctx, validImageName)
		// Closed image and a snapshot ID which doesn't belong to this image.
		_, err = validImg.GetSnapNamespaceType(snapInfo.Id)
		assert.Error(t, err)

		// Open image but invalid snap ID.
		validImg, err = OpenImage(ioctx, validImageName, NoSnapshot)
		assert.NoError(t, err)
		defer func() {
			assert.NoError(t, validImg.Close())
		}()

		_, err = validImg.GetSnapNamespaceType(uint64(22))
		assert.Error(t, err)

		// With non-existing image.
		invalidImageName := GetUUID()
		invalidImg := GetImage(ioctx, invalidImageName)
		_, err = invalidImg.GetSnapNamespaceType(snapInfo.Id)
		assert.Error(t, err)
	})

	t.Run("SnapNamespaceTypeUser", func(t *testing.T) {
		nsType, err := img.GetSnapNamespaceType(snapInfo.Id)
		assert.NoError(t, err)
		assert.Equal(t, nsType, SnapNamespaceTypeUser)
	})

	t.Run("SnapNamespaceTypeTrash", func(t *testing.T) {
		cloneName := "myClone"
		optionsClone := NewRbdImageOptions()
		defer optionsClone.Destroy()
		err := optionsClone.SetUint64(ImageOptionCloneFormat, 2)
		assert.NoError(t, err)

		// Create a clone of the image using the same snapshot.
		err = CloneImage(ioctx, imageName, snapName, ioctx, cloneName, optionsClone)
		assert.NoError(t, err)
		defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()

		// Once clone is created, remove the snapshot.
		err = snapshot.Remove()
		assert.NoError(t, err)

		// Snapshot would move to the trash because linked clone is still there.
		nsType, err := img.GetSnapNamespaceType(snapInfo.Id)
		assert.NoError(t, err)
		assert.Equal(t, nsType, SnapNamespaceTypeTrash)
	})
}

func TestGetSnapTrashNamespace(t *testing.T) {
	conn := radosConnect(t)
	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()

	imageName := "parent"
	snapName := "mySnap"
	cloneName := "myClone"
	options := NewRbdImageOptions()
	defer options.Destroy()
	err = options.SetUint64(ImageOptionOrder, uint64(testImageOrder))
	assert.NoError(t, err)
	err = options.SetUint64(ImageOptionFeatures, 1)
	assert.NoError(t, err)

	err = CreateImage(ioctx, imageName, testImageSize, options)
	assert.NoError(t, err)

	img, err := OpenImage(ioctx, imageName, NoSnapshot)
	assert.NoError(t, err)
	defer func() {
		assert.NoError(t, img.Close())
		assert.NoError(t, img.Remove())
	}()

	snapshot, err := img.CreateSnapshot(snapName)
	assert.NoError(t, err)

	optionsClone := NewRbdImageOptions()
	defer optionsClone.Destroy()
	err = optionsClone.SetUint64(ImageOptionCloneFormat, 2)
	assert.NoError(t, err)

	// Create a clone of the image using the same snapshot.
	err = CloneImage(ioctx, imageName, snapName, ioctx, cloneName, optionsClone)
	assert.NoError(t, err)
	defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()

	// Check the name of the snapshot.
	snapInfoList, err := img.GetSnapshotNames()
	assert.NoError(t, err)
	snapInfo := snapInfoList[0]
	assert.Equal(t, snapInfo.Name, snapName)

	// Remove the snapshot.
	err = snapshot.Remove()
	assert.NoError(t, err)

	// Snapshot would move to the trash because linked clone is still there.
	nsType, err := img.GetSnapNamespaceType(snapInfo.Id)
	assert.NoError(t, err)
	assert.Equal(t, nsType, SnapNamespaceTypeTrash)

	// Get the snap info again, name would have changed.
	newSnapInfoList, err := img.GetSnapshotNames()
	assert.NoError(t, err)
	newSnapInfo := newSnapInfoList[0]
	assert.NotEqual(t, newSnapInfo.Name, snapName)
	// ID would have remained same.
	assert.Equal(t, snapInfo.Id, newSnapInfo.Id)

	// Get the original name.
	origSnapName, err := img.GetSnapTrashNamespace(newSnapInfo.Id)
	assert.NoError(t, err)
	assert.Equal(t, snapName, origSnapName)

	invalidSnapID := uint64(22)

	t.Run("InvalidSnapID", func(t *testing.T) {
		_, err := img.GetSnapTrashNamespace(invalidSnapID)
		assert.Error(t, err)
	})

	t.Run("InvalidImage", func(t *testing.T) {
		invalidImgName := GetUUID()
		invalidImg := GetImage(ioctx, invalidImgName)
		_, err := invalidImg.GetSnapTrashNamespace(invalidSnapID)
		assert.Error(t, err)
	})
}
