package meta

import (
	"context"
	"path/filepath"
	"testing"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
	cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
	objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
	"github.com/stretchr/testify/require"
)

func TestInhumeECObject(t *testing.T) {
	t.Parallel()

	db := New(
		WithPath(filepath.Join(t.TempDir(), "metabase")),
		WithPermissions(0o600),
		WithEpochState(epochState{uint64(12)}),
	)

	require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
	require.NoError(t, db.Init(context.Background()))
	defer func() { require.NoError(t, db.Close(context.Background())) }()

	cnr := cidtest.ID()
	ecChunk := oidtest.ID()
	ecChunk2 := oidtest.ID()
	ecParent := oidtest.ID()
	tombstoneID := oidtest.ID()

	chunkObj := testutil.GenerateObjectWithCID(cnr)
	chunkObj.SetID(ecChunk)
	chunkObj.SetPayload([]byte{0, 1, 2, 3, 4})
	chunkObj.SetPayloadSize(uint64(5))
	chunkObj.SetECHeader(objectSDK.NewECHeader(objectSDK.ECParentInfo{ID: ecParent}, 0, 3, []byte{}, 0))

	chunkObj2 := testutil.GenerateObjectWithCID(cnr)
	chunkObj2.SetID(ecChunk2)
	chunkObj2.SetPayload([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
	chunkObj2.SetPayloadSize(uint64(10))
	chunkObj2.SetECHeader(objectSDK.NewECHeader(objectSDK.ECParentInfo{ID: ecParent}, 1, 3, []byte{}, 0))

	// put object with EC

	var prm PutPrm
	prm.SetObject(chunkObj)
	prm.SetStorageID([]byte("0/0"))
	_, err := db.Put(context.Background(), prm)
	require.NoError(t, err)

	prm.SetObject(chunkObj2)
	_, err = db.Put(context.Background(), prm)
	require.NoError(t, err)

	var ecChunkAddress oid.Address
	ecChunkAddress.SetContainer(cnr)
	ecChunkAddress.SetObject(ecChunk)

	var ecParentAddress oid.Address
	ecParentAddress.SetContainer(cnr)
	ecParentAddress.SetObject(ecParent)

	var chunkObjectAddress oid.Address
	chunkObjectAddress.SetContainer(cnr)
	chunkObjectAddress.SetObject(ecChunk)

	var getPrm GetPrm

	getPrm.SetAddress(ecChunkAddress)
	_, err = db.Get(context.Background(), getPrm)
	require.NoError(t, err)

	var ecInfoError *objectSDK.ECInfoError
	getPrm.SetAddress(ecParentAddress)
	_, err = db.Get(context.Background(), getPrm)
	require.ErrorAs(t, err, &ecInfoError)
	require.True(t, len(ecInfoError.ECInfo().Chunks) == 2 &&
		ecInfoError.ECInfo().Chunks[0].Index == 0 &&
		ecInfoError.ECInfo().Chunks[0].Total == 3)

	// inhume Chunk
	var inhumePrm InhumePrm
	var tombAddress oid.Address
	inhumePrm.SetAddresses(chunkObjectAddress)
	res, err := db.Inhume(context.Background(), inhumePrm)
	require.NoError(t, err)
	require.True(t, len(res.deletionDetails) == 1)
	require.True(t, res.deletionDetails[0].Size == 5)

	// inhume EC parent (like Delete does)
	tombAddress.SetContainer(cnr)
	tombAddress.SetObject(tombstoneID)
	inhumePrm.SetAddresses(ecParentAddress)
	inhumePrm.SetTombstoneAddress(tombAddress)
	res, err = db.Inhume(context.Background(), inhumePrm)
	require.NoError(t, err)
	// Previously deleted chunk shouldn't be in the details, because it is marked as garbage
	require.True(t, len(res.deletionDetails) == 1)
	require.True(t, res.deletionDetails[0].Size == 10)

	getPrm.SetAddress(ecParentAddress)
	_, err = db.Get(context.Background(), getPrm)
	require.ErrorAs(t, err, new(*apistatus.ObjectAlreadyRemoved))

	getPrm.SetAddress(ecChunkAddress)
	_, err = db.Get(context.Background(), getPrm)
	require.ErrorAs(t, err, new(*apistatus.ObjectAlreadyRemoved))
}