package container

import (
	"errors"
	"fmt"

	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
	cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
	containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
	"go.uber.org/zap"
)

func (cp *Processor) processSetEACL(e containerEvent.SetEACL) bool {
	if !cp.alphabetState.IsAlphabet() {
		cp.log.Info(logs.ContainerNonAlphabetModeIgnoreSetEACL)
		return true
	}

	err := cp.checkSetEACL(e)
	if err != nil {
		cp.log.Error(logs.ContainerSetEACLCheckFailed,
			zap.String("error", err.Error()),
		)

		return false
	}

	if err := cp.morphClient.NotarySignAndInvokeTX(e.NotaryRequest().MainTransaction); err != nil {
		cp.log.Error(logs.ContainerCouldNotApproveSetEACL,
			zap.String("error", err.Error()),
		)
		return false
	}

	return true
}

func (cp *Processor) checkSetEACL(e containerEvent.SetEACL) error {
	binTable := e.Table()

	// unmarshal table
	table := eacl.NewTable()

	err := table.Unmarshal(binTable)
	if err != nil {
		return fmt.Errorf("invalid binary table: %w", err)
	}

	idCnr, ok := table.CID()
	if !ok {
		return errors.New("missing container ID in eACL table")
	}

	// receive owner of the related container
	cnr, err := cntClient.Get(cp.cnrClient, idCnr)
	if err != nil {
		return fmt.Errorf("could not receive the container: %w", err)
	}

	// ACL extensions can be disabled by basic ACL, check it
	if !cnr.Value.BasicACL().Extendable() {
		return errors.New("ACL extension disabled by container basic ACL")
	}

	err = cp.verifySignature(signatureVerificationData{
		ownerContainer:  cnr.Value.Owner(),
		verb:            session.VerbContainerSetEACL,
		idContainerSet:  true,
		idContainer:     idCnr,
		binTokenSession: e.SessionToken(),
		binPublicKey:    e.PublicKey(),
		signature:       e.Signature(),
		signedData:      binTable,
	})
	if err != nil {
		return fmt.Errorf("auth eACL table setting: %w", err)
	}

	return nil
}