package plugin

import (
	"context"
	"math"
	"os"

	"google.golang.org/grpc"

	"github.com/hashicorp/go-hclog"
	log "github.com/hashicorp/go-hclog"
	plugin "github.com/hashicorp/go-plugin"

	"gitlab.com/gitlab-org/fleeting/fleeting/internal/plugin/proto"
	"gitlab.com/gitlab-org/fleeting/fleeting/provider"
)

//go:generate protoc -I ./ ./proto/instance_group.proto --go_out=./
//go:generate protoc -I ./ ./proto/instance_group.proto --go-grpc_out=./

var (
	_ plugin.Plugin     = (*GRPCInstanceGroupPlugin)(nil)
	_ plugin.GRPCPlugin = (*GRPCInstanceGroupPlugin)(nil)
)

var Handshake = plugin.HandshakeConfig{
	ProtocolVersion:  0,
	MagicCookieKey:   "FLEETING_INSTANCE_GROUP_PLUGIN",
	MagicCookieValue: "4c6410b1-cb78-4962-8c76-ab936d0ea908",
}

func Serve(impl provider.InstanceGroup) {
	plugin.Serve(&plugin.ServeConfig{
		HandshakeConfig: Handshake,
		Logger: log.New(&log.LoggerOptions{
			Level:      log.Trace,
			Output:     os.Stderr,
			JSONFormat: true,
		}),
		VersionedPlugins: map[int]plugin.PluginSet{
			0: {
				"instancegroup": &GRPCInstanceGroupPlugin{Impl: impl},
			},
		},
		GRPCServer: func(opts []grpc.ServerOption) *grpc.Server {
			opts = append(opts, grpc.MaxRecvMsgSize(math.MaxInt32))
			opts = append(opts, grpc.MaxSendMsgSize(math.MaxInt32))
			return plugin.DefaultGRPCServer(opts)
		},
	})
}

type GRPCInstanceGroupPlugin struct {
	plugin.NetRPCUnsupportedPlugin

	Impl   provider.InstanceGroup
	Logger hclog.Logger
	Config []byte
}

func (b GRPCInstanceGroupPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
	proto.RegisterInstanceGroupServer(s, &instanceGroupGRPCServer{
		Logger: b.Logger,
		Impl:   b.Impl,
	})

	return nil
}

func (b *GRPCInstanceGroupPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
	return &instanceGroupGRPCClient{
		client: proto.NewInstanceGroupClient(c),
		config: b.Config,
	}, nil
}
