package blobstor

import (
	"context"
	"os"
	"testing"

	objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
	cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
	objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
	oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
	"github.com/stretchr/testify/require"
)

func TestExists(t *testing.T) {
	dir, err := os.MkdirTemp("", "frostfs*")
	require.NoError(t, err)
	t.Cleanup(func() { _ = os.RemoveAll(dir) })

	const smallSizeLimit = 512

	storages, _, largeFileStorage := defaultTestStorages(dir, smallSizeLimit)

	b := New(WithStorages(storages))

	require.NoError(t, b.Open(false))
	require.NoError(t, b.Init())

	objects := []*objectSDK.Object{
		testObject(smallSizeLimit / 2),
		testObject(smallSizeLimit + 1),
	}

	for i := range objects {
		var prm common.PutPrm
		prm.Object = objects[i]
		_, err := b.Put(context.Background(), prm)
		require.NoError(t, err)
	}

	var prm common.ExistsPrm
	for i := range objects {
		prm.Address = objectCore.AddressOf(objects[i])

		res, err := b.Exists(context.Background(), prm)
		require.NoError(t, err)
		require.True(t, res.Exists)
	}

	prm.Address = oidtest.Address()
	res, err := b.Exists(context.Background(), prm)
	require.NoError(t, err)
	require.False(t, res.Exists)

	t.Run("corrupt directory", func(t *testing.T) {
		largeFileStorage.SetOption(teststore.WithExists(func(common.ExistsPrm) (common.ExistsRes, error) {
			return common.ExistsRes{}, teststore.ErrDiskExploded
		}))

		// Object exists, first error is logged.
		prm.Address = objectCore.AddressOf(objects[0])
		res, err := b.Exists(context.Background(), prm)
		require.NoError(t, err)
		require.True(t, res.Exists)

		// Object doesn't exist, first error is returned.
		prm.Address = objectCore.AddressOf(objects[1])
		_, err = b.Exists(context.Background(), prm)
		require.Error(t, err)
		require.ErrorIs(t, err, teststore.ErrDiskExploded)
	})
}

func testObject(sz uint64) *objectSDK.Object {
	raw := objectSDK.New()

	raw.SetID(oidtest.ID())
	raw.SetContainerID(cidtest.ID())

	raw.SetPayload(make([]byte, sz))

	// fit the binary size to the required
	data, _ := raw.Marshal()
	if ln := uint64(len(data)); ln > sz {
		raw.SetPayload(raw.Payload()[:sz-(ln-sz)])
	}

	return raw
}