package shared

import (
	"errors"
	"io"

	"golang.org/x/net/context"

	mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin"
	gpb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/sirupsen/logrus"
)

type DeviceModelClient struct{ client pb.PluginClient }

func (m *DeviceModelClient) Unmarshal(json []byte, path *gpb.Path) error {
	_, err := m.client.Unmarshal(context.Background(), &pb.UnmarshalRequest{
		Json: json,
		Path: path,
	})
	return err
}

func (m *DeviceModelClient) SetNode(path *gpb.Path, value *gpb.TypedValue) error {
	_, err := m.client.SetNode(context.Background(), &pb.SetNodeRequest{
		Path:  path,
		Value: value,
	})
	return err
}

func (m *DeviceModelClient) GetNode(path *gpb.Path, requestForIntendedState bool) ([]*gpb.Notification, error) {
	resp, err := m.client.GetNode(context.Background(), &pb.GetNodeRequest{
		Path:                    path,
		RequestForIntendedState: requestForIntendedState,
	})
	return resp.GetNodes(), err
}

func (m *DeviceModelClient) DeleteNode(path *gpb.Path) error {
	_, err := m.client.DeleteNode(context.Background(), &pb.DeleteNodeRequest{
		Path: path,
	})
	return err
}

func (m *DeviceModelClient) Model(filterReadOnly bool) ([]byte, error) {
	resp, err := m.client.Model(context.Background(), &pb.ModelRequest{
		FilterReadOnly: filterReadOnly,
	})
	return resp.Json, err
}

func (m *DeviceModelClient) Diff(original, modified []byte) (*gpb.Notification, error) {
	resp, err := m.client.Diff(context.Background(), &pb.DiffRequest{
		Original: original,
		Modified: modified,
	})
	return resp.GetNotification(), err
}

func (m *DeviceModelClient) ValidateChange(operation mnepb.ApiOperation, path *gpb.Path, value *gpb.TypedValue) ([]byte, error) {
	resp, err := m.client.ValidateChange(context.Background(), &pb.ValidateChangeRequest{
		Operation: operation,
		Path:      path,
		Value:     value,
	})
	return resp.GetModel(), err
}

func (m *DeviceModelClient) PruneConfigFalse(value []byte) ([]byte, error) {
	resp, err := m.client.PruneConfigFalse(context.Background(), &pb.PruneConfigFalseRequest{
		Value: value,
	})
	return resp.GetModel(), err
}

func (m *DeviceModelClient) SchemaTreeGzip() ([]byte, error) {
	schemaTreeGzipClient, err := m.client.SchemaTreeGzip(context.Background(), &pb.SchemaTreeGzipRequest{})
	if err != nil {
		return nil, err
	}

	sTreeBytes := []byte{}

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

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

			return nil, err
		}
		sTreeBytes = append(sTreeBytes, payload.Chunk...)
	}

	return sTreeBytes, nil
}
