package subnetevents

import (
	"errors"
	"fmt"

	"github.com/TrueCloudLab/frostfs-sdk-go/subnet"
	subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
	"github.com/TrueCloudLab/frostfs-sdk-go/user"
)

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

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

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

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

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

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

// Assert processes the attempt to create a subnet. It approves the creation through nil return.
//
// All read errors of Put are forwarded.
//
// It 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 FrostFS system
	var creator user.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 the explicit ID equals to the one from info
	if !subnet.AssertReference(info, id) {
		return errDiffID
	}

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

	return nil
}