package container

import (
	"context"
	"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(ctx context.Context, cnr cid.ID) (*containercore.DelInfo, error) {
	return DeletionInfo(ctx, (*Client)(x), cnr)
}

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

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

	return c.DeletionInfo(ctx, binCnr)
}

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

	res, err := c.client.TestInvoke(ctx, prm)
	if err != nil {
		if strings.Contains(err.Error(), containerContract.NotFoundError) {
			return nil, new(apistatus.ContainerNotFound)
		}
		return nil, fmt.Errorf("test invoke (%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("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("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("decode container owner id (%s): %w", deletionInfoMethod, err)
	}

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

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