package container

import (
	"crypto/sha256"
	"fmt"
	"strings"

	containerContract "git.frostfs.info/TrueCloudLab/frostfs-contract/container"
	containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
	"github.com/mr-tron/base58"
)

func (x *containerSource) DeletionInfo(cnr cid.ID) (*containercore.DelInfo, error) {
	return DeletionInfo((*Client)(x), cnr)
}

type deletionInfo interface {
	DeletionInfo(cid []byte) (*containercore.DelInfo, error)
}

func DeletionInfo(c deletionInfo, cnr cid.ID) (*containercore.DelInfo, error) {
	binCnr := make([]byte, sha256.Size)
	cnr.Encode(binCnr)

	return c.DeletionInfo(binCnr)
}

func (c *Client) DeletionInfo(cid []byte) (*containercore.DelInfo, error) {
	prm := client.TestInvokePrm{}
	prm.SetMethod(deletionInfoMethod)
	prm.SetArgs(cid)

	res, err := c.client.TestInvoke(prm)
	if err != nil {
		if strings.Contains(err.Error(), containerContract.NotFoundError) {
			return nil, new(apistatus.ContainerNotFound)
		}
		return nil, fmt.Errorf("could not perform test invocation (%s): %w", deletionInfoMethod, err)
	} else if ln := len(res); ln != 1 {
		return nil, fmt.Errorf("unexpected stack item count (%s): %d", deletionInfoMethod, ln)
	}

	arr, err := client.ArrayFromStackItem(res[0])
	if err != nil {
		return nil, fmt.Errorf("could not get item array of container (%s): %w", deletionInfoMethod, err)
	}

	if len(arr) != 2 {
		return nil, fmt.Errorf("unexpected container stack item count (%s): %d", deletionInfoMethod, len(arr))
	}

	rawOwner, err := client.BytesFromStackItem(arr[0])
	if err != nil {
		return nil, fmt.Errorf("could not get byte array of container (%s): %w", deletionInfoMethod, err)
	}

	var owner user.ID
	if err := owner.DecodeString(base58.Encode(rawOwner)); err != nil {
		return nil, fmt.Errorf("could not decode container owner id (%s): %w", deletionInfoMethod, err)
	}

	epoch, err := client.BigIntFromStackItem(arr[1])
	if err != nil {
		return nil, fmt.Errorf("could not get byte array of container signature (%s): %w", deletionInfoMethod, err)
	}

	return &containercore.DelInfo{
		Owner: owner,
		Epoch: epoch.Uint64(),
	}, nil
}