package initialize

import (
	"fmt"

	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
	"github.com/nspcc-dev/neo-go/pkg/core/state"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
)

func deployContracts(c *helper.InitializeContext) error {
	alphaCs := c.GetContract(constants.AlphabetContract)

	var keysParam []any

	baseGroups := alphaCs.Manifest.Groups

	// alphabet contracts should be deployed by individual nodes to get different hashes.
	for i, acc := range c.Accounts {
		ctrHash := state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name)
		if c.IsUpdated(ctrHash, alphaCs) {
			c.Command.Printf("Alphabet contract #%d is already deployed.\n", i)
			continue
		}

		alphaCs.Manifest.Groups = baseGroups
		err := helper.AddManifestGroup(c.ContractWallet, ctrHash, alphaCs)
		if err != nil {
			return fmt.Errorf("can't sign manifest group: %v", err)
		}

		keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
		params := helper.GetContractDeployParameters(alphaCs, c.GetAlphabetDeployItems(i, len(c.Wallets)))

		act, err := actor.NewSimple(c.Client, acc)
		if err != nil {
			return fmt.Errorf("could not create actor: %w", err)
		}

		txHash, vub, err := act.SendCall(management.Hash, constants.DeployMethodName, params...)
		if err != nil {
			return fmt.Errorf("can't deploy alphabet #%d contract: %w", i, err)
		}

		c.SentTxs = append(c.SentTxs, helper.HashVUBPair{Hash: txHash, Vub: vub})
	}

	for _, ctrName := range constants.ContractList {
		cs := c.GetContract(ctrName)

		ctrHash := cs.Hash
		if c.IsUpdated(ctrHash, cs) {
			c.Command.Printf("%s contract is already deployed.\n", ctrName)
			continue
		}

		err := helper.AddManifestGroup(c.ContractWallet, ctrHash, cs)
		if err != nil {
			return fmt.Errorf("can't sign manifest group: %v", err)
		}

		args, err := helper.GetContractDeployData(c, ctrName, keysParam, constants.DeployMethodName)
		if err != nil {
			return fmt.Errorf("%s: getting deploy params: %v", ctrName, err)
		}
		params := helper.GetContractDeployParameters(cs, args)
		res, err := c.CommitteeAct.MakeCall(management.Hash, constants.DeployMethodName, params...)
		if err != nil {
			return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
		}

		if err := c.SendCommitteeTx(res.Script, false); err != nil {
			return err
		}
	}

	return c.AwaitTx()
}