package commands

import (
	pb "code.fbi.h-da.de/cocsn/gosdn/api/proto"
	"code.fbi.h-da.de/cocsn/gosdn/log"
	"context"
	"github.com/rivo/tview"
	grpc "google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/emptypb"
	"time"
)

const (
	defaultName = "gosdn-cli"
)

type command struct {
	Name        string
	Description string
	Function    func(conn *grpc.ClientConn) string
}

//CommandList contains the specific goSDN gRPC calls
var CommandList = []command{
	{"hello", "test connection to goSDN controller", goSDNSayHello},
	{"shutdown", "request goSDN controller to shutdown", goSDNShutdown},
	{"testdb", "test all database connections", goSDNTestDB},
	{"tapigetedge", "get list of edges", TAPIGetEdge},
	{"tapigetedgenode", "get list of edgenodes", TAPIGetEdgeNode},
	{"tapigetlink", "get list of links", TAPIGetLink},
}

//Connect creates a new connection to the gRPC server
func Connect(address string) (*grpc.ClientConn, error) {
	//address := "141.100.70.170:55066"
	return grpc.Dial(address, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second), grpc.WithBlock())
}

func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error {
	var streamError error
	c := pb.NewGrpcCliClient(conn)
	stream, err := c.CreateLogStream(context.Background(), &emptypb.Empty{})
	if err != nil {
		log.Error(err)
	}

	go func(stream pb.GrpcCli_CreateLogStreamClient) {
		for {
			msg, err := stream.Recv()
			if err != nil {
				streamError = err
				break
			}
			response := []byte(msg.Log + "\n")
			clv.Write(response)
		}
	}(stream)

	return streamError
}

func goSDNSayHello(conn *grpc.ClientConn) string {
	c := pb.NewGrpcCliClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatal(err)
	}
	return r.GetMessage()
}

func goSDNShutdown(conn *grpc.ClientConn) string {

	c := pb.NewGrpcCliClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name})
	if err != nil {
		log.Fatal(err)
	}
	return r.GetMessage()
}

func goSDNTestDB(conn *grpc.ClientConn) string {
	// TODO: fill with code and also see if grpc interface has this stub implemented.
	return "not implemented yet"
}

// TAPIGetEdge triggers the GetEdge function of the Ciena
// flavoured TAPI client
func TAPIGetEdge(conn *grpc.ClientConn) string {

	c := pb.NewGrpcCliClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name})
	if err != nil {
		log.Fatal(err)
	}
	return r.GetMessage()
}

// TAPIGetEdgeNode triggers the GetEdgeNode function of the Ciena
// flavoured TAPI client
func TAPIGetEdgeNode(conn *grpc.ClientConn) string {
	c := pb.NewGrpcCliClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name})
	if err != nil {
		log.Fatal(err)
	}
	return r.GetMessage()
}

// TAPIGetLink triggers the GetLink function of the Ciena
// flavoured TAPI client
func TAPIGetLink(conn *grpc.ClientConn) string {

	c := pb.NewGrpcCliClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name})
	if err != nil {
		log.Fatal(err)
	}
	return r.GetMessage()
}