package api

import (
	"context"
	"errors"
	"io"
	"time"

	pib "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
	pipb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
	prb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-registry"
	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
	"github.com/google/uuid"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/ygot"
	"github.com/sirupsen/logrus"
)

// GetAvailablePlugins requests all available plugins that can be registered
// within the controller. The set of available plugins is based on the
// plugin-registry in use.
func GetAvailablePlugins(ctx context.Context, addr string) (*prb.GetResponse, error) {
	pluginRegistryClient, err := nbi.PluginClient(addr, dialOptions...)
	if err != nil {
		return nil, err
	}

	req := &pib.GetAvailablePluginsRequest{
		Timestamp: time.Now().UnixNano(),
	}

	return pluginRegistryClient.AvailablePlugins(ctx, req)
}

// GetPluginSchemaTree gets the schema tree for a plugin.
func GetPluginSchemaTree(ctx context.Context, addr string, pluginID uuid.UUID) (map[string]*yang.Entry, error) {
	pluginClient, err := nbi.PluginClient(addr, dialOptions...)
	if err != nil {
		return map[string]*yang.Entry{}, err
	}

	req := &pipb.GetPluginSchemaRequest{
		Timestamp: time.Now().UnixNano(),
		Pid:       pluginID.String(),
	}

	ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
	defer cancel()
	sClient, err := pluginClient.GetPluginSchema(ctx, req)
	if err != nil {
		return map[string]*yang.Entry{}, err
	}

	sTreeBytes := []byte{}

	for {
		payload, err := sClient.Recv()
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			logrus.Error(err)

			closeErr := sClient.CloseSend()
			if closeErr != nil {
				return nil, err
			}

			return map[string]*yang.Entry{}, err
		}
		sTreeBytes = append(sTreeBytes, payload.Chunk...)
	}

	sTreeMap, err := ygot.GzipToSchema(sTreeBytes)
	if err != nil {
		return map[string]*yang.Entry{}, err
	}

	return sTreeMap, nil
}