package subnetevents

import (
	"errors"
	"fmt"

	"github.com/nspcc-dev/neofs-sdk-go/owner"
	"github.com/nspcc-dev/neofs-sdk-go/subnet"
	subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
)

// Put represents notification about NeoFS subnet creation.
// Generated by a contract when intending to create a subnet.
type Put interface {
	// Contains ID of the subnet to be created.
	eventWithID

	// ReadCreator reads user ID of the subnet creator.
	// Returns an error if ID is missing.
	ReadCreator(id *owner.ID) error

	// ReadInfo reads information about subnet to be created.
	ReadInfo(info *subnet.Info) error
}

// PutValidator asserts intent to create a subnet.
type PutValidator struct{}

// errDiffOwner is returned when subnet owners differ.
var errDiffOwner = errors.New("diff subnet owners")

// errDiffID is returned when subnet IDs differ.
var errDiffID = errors.New("diff subnet IDs")

// Assert processes the attempt to create a subnet. Approves the creation through nil return.
//
// All read errors of Put are forwarded.
//
// Returns an error on:
//   * zero subnet creation;
//   * empty ID or different from the one wired into info;
//   * empty owner ID or different from the one wired into info.
func (x PutValidator) Assert(event Put) error {
	var err error

	// read ID
	var id subnetid.ID
	if err = event.ReadID(&id); err != nil {
		return fmt.Errorf("read ID: %w", err)
	}

	// prevent zero subnet creation
	if subnetid.IsZero(id) {
		return zeroSubnetOp{
			op: "creation",
		}
	}

	// read creator's user ID in NeoFS system
	var creator owner.ID
	if err = event.ReadCreator(&creator); err != nil {
		return fmt.Errorf("read creator: %w", err)
	}

	// read information about the subnet
	var info subnet.Info
	if err = event.ReadInfo(&info); err != nil {
		return fmt.Errorf("read info: %w", err)
	}

	// check if explicit ID equals to the one from info
	if !subnet.IDEquals(info, id) {
		return errDiffID
	}

	// check if explicit creator equals to the one from info
	if !subnet.IsOwner(info, creator) {
		return errDiffOwner
	}

	return nil
}