package networkelement

import (
	"context"

	"code.fbi.h-da.de/danet/gosdn/controller/conflict"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/transport"
	"github.com/google/uuid"
	gpb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/ygot/ygot"
	log "github.com/sirupsen/logrus"
	"google.golang.org/protobuf/proto"

	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
)

// NetworkElement represents an Managed Network Element (MNE) which is managed by
// nucleus.
type NetworkElement interface {
	ID() uuid.UUID
	GetModel() ([]byte, error)
	GetPlugin() plugin.Plugin
	GetModelAsFilteredCopy() ([]byte, error)
	Transport() transport.Transport
	Name() string
	ProcessResponse(proto.Message) error
	IsTransportValid() bool
	GetModelAsString() (string, error)
	TransportAddress() string
	GetMetadata() conflict.Metadata
	PndID() uuid.UUID
}

// Details contains details of a network element used by the cSBI mechanism.
type Details struct {
	ID              string
	Address         string
	TransportOption *tpb.TransportOption
}

// LoadedNetworkElement represents a Managed Network Element that was loaeded
// by using the Load() method of the NetworkElementStore.
type LoadedNetworkElement struct {
	// ID represents the UUID of the LoadedNetworkElement.
	ID string `json:"id" bson:"_id"`
	// Name represents the name of the LoadedNetworkElement.
	Name string `json:"name,omitempty"`
	// TransportType represent the type of the transport in use of the LoadedNetworkElement.
	TransportType string `json:"transport_type,omitempty" bson:"transport_type,omitempty"`
	// TransportAddress represents the address from which the network element can be reached via the transport method.
	TransportAddress string `json:"transport_address,omitempty" bson:"transport_address,omitempty"`
	// TransportUsername is used for authentication via the transport method in use.
	TransportUsername string `json:"transport_username,omitempty" bson:"transport_username,omitempty"`
	// TransportPassword is used for authentication via the transport method in use.
	TransportPassword   string `json:"transport_password,omitempty" bson:"transport_password,omitempty"`
	TransportOptionCsbi bool   `json:"transport_option_csbi,omitempty" bson:"transport_option_csbi,omitempty"`

	// SBI indicates the southbound interface, which is used by this network element as UUID.
	Plugin string `json:"plugin"`
	Model  string `json:"model,omitempty" bson:"model,omitempty"`

	Metadata conflict.Metadata `json:"metadata" bson:"metadata"`

	PndID string `json:"pnd_id" bson:"pnd_id"`
}

// EnsureIntendedConfigurationIsAppliedOnNetworkElement pushes the stored
// configuration to a network element.
// TODO: find a better place for this function.
func EnsureIntendedConfigurationIsAppliedOnNetworkElement(mne NetworkElement) error {
	model, err := mne.GetModelAsFilteredCopy()
	if err != nil {
		return err
	}

	req := &gpb.SetRequest{}
	path, err := ygot.StringToStructuredPath("/")
	if err != nil {
		return err
	}

	req.Update = []*gpb.Update{{
		Path: path,
		Val: &gpb.TypedValue{
			Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: []byte(model)},
		},
	}}

	response, err := mne.Transport().CustomSet(context.Background(), req)
	if err != nil {
		log.Errorf("Failed to apply model of network element err=%+v, response=%+v", err, response)
		return err
	}

	return nil
}
