package rangehashsvc

import (
	"context"
	"crypto/sha256"
	"fmt"
	"hash"

	"github.com/nspcc-dev/neofs-api-go/pkg"
	"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore"
	"github.com/nspcc-dev/neofs-node/pkg/util"
	"github.com/nspcc-dev/tzhash/tz"
	"github.com/pkg/errors"
)

type localHasher struct {
	storage *localstore.Storage
}

func (h *localHasher) hashRange(ctx context.Context, prm *Prm, handler func([][]byte)) error {
	obj, err := h.storage.Get(prm.addr)
	if err != nil {
		return errors.Wrapf(err, "(%T) could not get object from local storage", h)
	}

	payload := obj.Payload()
	hashes := make([][]byte, 0, len(prm.rngs))

	var hasher hash.Hash
	switch prm.typ {
	default:
		panic(fmt.Sprintf("unexpected checksum type %v", prm.typ))
	case pkg.ChecksumSHA256:
		hasher = sha256.New()
	case pkg.ChecksumTZ:
		hasher = tz.New()
	}

	for i := range prm.rngs {
		left := prm.rngs[i].GetOffset()
		right := left + prm.rngs[i].GetLength()

		if ln := uint64(len(payload)); ln < right {
			return errors.Errorf("(%T) object range is out-of-boundaries (size %d, range [%d:%d]", h, ln, left, right)
		}

		hasher.Reset()

		hasher.Write(util.SaltXOR(payload[left:right], prm.salt))

		hashes = append(hashes, hasher.Sum(nil))
	}

	handler(hashes)

	return nil
}