package nucleus

import (
	"go.mongodb.org/mongo-driver/bson"

	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"

	"github.com/google/uuid"
	log "github.com/sirupsen/logrus"
)

// NewPNDEntity creates a PND object as it is stored in the storage.
func NewPND(pndID uuid.UUID, name string, description string) networkdomain.NetworkDomain {
	pnd := &pndImplementation{
		Id:          pndID,
		Name:        name,
		Description: description,
	}

	return pnd
}

type pndImplementation struct {
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	//nolint
	Id uuid.UUID `json:"id,omitempty"`

	// csbiClient cpb.CsbiServiceClient
	// callback   func(uuid.UUID, chan networkelement.Details)
}

func (pnd *pndImplementation) ID() uuid.UUID {
	return pnd.Id
}

// GetName returns the name of the PND.
func (pnd *pndImplementation) GetName() string {
	return pnd.Name
}

// GetDescription returns the current description of the PND.
func (pnd *pndImplementation) GetDescription() string {
	return pnd.Description
}

// nolint
// handleRollbackError will be implemented in the near future
func handleRollbackError(id uuid.UUID, err error) {
	log.Error(err)
	// TODO: Notion of invalid state needed.
}

// func (pnd *pndImplementation) handleCsbiDeletion(mne networkelement.NetworkElement) error {
// 	log.Infof("csbi deletion triggered for %v", mne.ID().String())
// 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
// 	defer cancel()
// 	req := &cpb.DeleteRequest{
// 		Timestamp: time.Now().UnixNano(),
// 		Did:       []string{mne.ID().String()},
// 	}
// 	resp, err := pnd.csbiClient.Delete(ctx, req)
// 	if err != nil {
// 		return err
// 	}
// 	log.WithFields(log.Fields{
// 		"uuid":   mne.ID().String(),
// 		"status": resp.Status,
// 	}).Info("csbi deleted")
// 	return nil
// }

//NOTE: csbi is currently not support; will be removed in the near future.
//func (pnd *pndImplementation) handleCsbiEnrolment(name string, opt *tpb.TransportOption) (uuid.UUID, error) {
//	g := new(errgroup.Group)
//	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
//	defer cancel()
//	req := &cpb.CreateRequest{
//		Timestamp:       time.Now().UnixNano(),
//		TransportOption: []*tpb.TransportOption{opt},
//	}
//	resp, err := pnd.csbiClient.Create(ctx, req)
//	if err != nil {
//		return uuid.Nil, err
//	}
//	// the slice only contains one deployment
//	var mneID uuid.UUID
//	for _, deployment := range resp.Deployments {
//		dCopy := deployment
//		g.Go(func() error {
//			mneID, err = pnd.createCsbiNetworkElement(ctx, name, dCopy, opt)
//			if err != nil {
//				return err
//			}
//			return nil
//		})
//	}
//	err = g.Wait()
//	if err != nil {
//		return uuid.Nil, err
//	}
//
//	return mneID, nil
//}

//NOTE: csbi is currently not support; will be removed in the near future.
// createCsbiNetworkElement is a helper method for cSBI network element creation. The method
// waits for a SYN (which indicates that the cSBI is running and addressable)
// of the commissioned cSBI and creates the network element within the controller.
//func (pnd *pndImplementation) createCsbiNetworkElement(
//	ctx context.Context,
//	name string,
//	deployment *cpb.Deployment,
//	opt *tpb.TransportOption,
//) (uuid.UUID, error) {
//	//id, err := uuid.Parse(deployment.Id)
//	//if err != nil {
//	//	return uuid.Nil, err
//	//}
//	//ch := make(chan networkelement.Details, 1)
//	//pnd.callback(id, ch)
//	//defer pnd.callback(id, nil)
//	//defer close(ch)
//	//tickatus := time.NewTicker(time.Minute * 1)
//	//defer tickatus.Stop()
//	//select {
//	//case <-tickatus.C:
//	//	log.WithFields(log.Fields{
//	//		"id":  deployment.Id,
//	//		"err": ctx.Err(),
//	//	}).Error("csbi handshake timed out")
//	//case mneDetails := <-ch:
//	//	log.Infof("syn from csbi %v", mneDetails.ID)
//	//	id, err := uuid.Parse(mneDetails.ID)
//	//	if err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//	csbiTransportOptions := &tpb.TransportOption{
//	//		Address:         mneDetails.Address,
//	//		Username:        opt.Username,
//	//		Password:        opt.Password,
//	//		Tls:             opt.Tls,
//	//		Type:            opt.Type,
//	//		TransportOption: opt.TransportOption,
//	//	}
//	//	log.WithField("transport option", csbiTransportOptions).Debug("gosdn gnmi transport options")
//	//
//	//	files := []string{util.GoStructName, util.ManifestFileName, util.GoStructAdditionsName}
//	//	csbiID := uuid.New()
//	//	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
//	//	defer cancel()
//	//
//	//	g := new(errgroup.Group)
//	//	for _, f := range files {
//	//		req := &cpb.GetPayloadRequest{
//	//			Timestamp: time.Now().UnixNano(),
//	//			Did:       deployment.Id,
//	//			File:      f,
//	//		}
//	//		g.Go(func() error {
//	//			gClient, err := pnd.csbiClient.GetFile(ctx, req)
//	//			if err != nil {
//	//				return err
//	//			}
//	//			err = saveStreamToFile(gClient, req.GetFile(), csbiID)
//	//			if err != nil {
//	//				return err
//	//			}
//	//			return nil
//	//		})
//	//	}
//	//
//	//	err = g.Wait()
//	//	if err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//
//	//	csbi, err := NewSBI(spb.Type_TYPE_CONTAINERISED, csbiID)
//	//	if err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//	err = pnd.southboundService.Add(csbi)
//	//	if err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//	mne, err := NewNetworkElement(name, uuid.Nil, csbiTransportOptions, csbi, conflict.Metadata{ResourceVersion: 0})
//	//	if err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//	mne.(*CsbiNetworkElement).UUID = id
//	//	ch <- networkelement.Details{TransportOption: opt}
//	//	if err := pnd.networkElementService.Add(mne); err != nil {
//	//		return uuid.Nil, err
//	//	}
//	//	return id, nil
//	//}
//	return uuid.Nil, nil
//}

// MarshalBSON implements the MarshalBSON interface to store a network element as BSON.
func (pnd *pndImplementation) MarshalBSON() ([]byte, error) {
	return bson.Marshal(&struct {
		ID          string `bson:"_id"`
		Name        string `bson:"name"`
		Description string `bson:"description"`
	}{
		ID:          pnd.Id.String(),
		Name:        pnd.Name,
		Description: pnd.Description,
	})
}
