package test

import (
	"net"
	"reflect"

	"code.fbi.h-da.de/danet/gosdn/forks/google/gnmi"
	oc "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig"
	pb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/util"
	"github.com/openconfig/ygot/ygot"
	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

type server struct {
	*gnmi.Server
}

func callback(newConfig ygot.ValidatedGoStruct) error {
	// Apply the config to your network element and return nil if success. return error if fails.
	//
	// Do something ...
	return nil
}

func newServer(model *gnmi.Model, config []byte) (*server, error) {
	s, err := gnmi.NewServer(model, config, callback)
	if err != nil {
		return nil, err
	}
	return &server{Server: s}, nil
}

/*
TODO: Implement multiple server configurations
// Get overrides the Get func of gnmi.Target to provide user auth.
func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) {
	msg, ok := credentials.AuthorizeUser(ctx)
	if !ok {
		log.Infof("denied a Get request: %v", msg)
		return nil, status.Error(codes.PermissionDenied, msg)
	}
	log.Infof("allowed a Get request: %v", msg)
	return s.Server.Get(ctx, req)
}

// Set overrides the Set func of gnmi.Target to provide user auth.
func (s *server) Set(ctx context.Context, req *pb.SetRequest) (*pb.SetResponse, error) {
	msg, ok := credentials.AuthorizeUser(ctx)
	if !ok {
		log.Infof("denied a Set request: %v", msg)
		return nil, status.Error(codes.PermissionDenied, msg)
	}
	log.Infof("allowed a Set request: %v", msg)
	return s.Server.Set(ctx, req)
}
*/

func GnmiTarget(stop chan bool, bindAddr string) error {
	if bindAddr == "" {
		bindAddr = "localhost:13371"
	}

	entries := make([]*yang.Entry, 0)
	for _, e := range oc.SchemaTree {
		entries = append(entries, e)
	}

	modelData, err := util.FindModelData(entries)
	if err != nil {
		log.Error(err)
	}

	// Google stuff from here
	model := gnmi.NewModel(
		modelData,
		reflect.TypeOf((*oc.Device)(nil)),
		oc.SchemaTree["Device"],
		oc.Unmarshal,
		oc.ΛEnum)

	g := grpc.NewServer()

	var configData []byte
	s, err := newServer(model, configData)
	if err != nil {
		log.Errorf("error in creating gnmi target: %v", err)
		return err
	}
	pb.RegisterGNMIServer(g, s)
	reflection.Register(g)

	log.Infof("starting to listen on %s", bindAddr)
	listen, err := net.Listen("tcp", bindAddr)
	if err != nil {
		log.Errorf("failed to listen: %v", err)
		return err
	}

	log.Info("starting to serve")
	go func() {
		<-stop
		g.GracefulStop()
	}()
	if err := g.Serve(listen); err != nil {
		log.Errorf("failed to serve: %v", err)
		return err
	}
	return nil
}
