package jsonrpc

import (
	"encoding/json"
	"log"
	"strings"

	"golang.org/x/net/context"
	"google.golang.org/grpc"

	serverGrpc "git.loafle.net/overflow/overflow_api_server/golang"
	"git.loafle.net/overflow/overflow_service_websocket/config"
	"git.loafle.net/overflow/overflow_service_websocket/pool"
	grpcPool "git.loafle.net/overflow/overflow_service_websocket/pool/grpc"
	"git.loafle.net/overflow/overflow_service_websocket/protocol"
)

type Request struct {
	Method string   `json:"method"`
	Params []string `json:"params,omitempty"`
}

type Response struct {
	Result string `json:"result,omitempty"`
}

// NewHandler returns a new JSON RPC handler.
func NewHandler() protocol.Handler {
	c := config.GetConfig().GRpc

	addr := c.Addr

	servicePool, err := grpcPool.New(1, 5,
		func(conn *grpc.ClientConn) (interface{}, error) {
			return serverGrpc.NewOverflowApiServerClient(conn), nil
		},
		func() (*grpc.ClientConn, error) {
			return grpc.Dial(addr, grpc.WithInsecure())
		},
	)
	if nil != err {
		log.Fatal(err)
		return nil
	}
	h := &handler{
		pool: servicePool,
	}

	return h
}

// Codec creates a CodecRequest to process each request.
type handler struct {
	pool pool.Pool
}

// NewRequest returns a CodecRequest.
func (h *handler) Handle(data []byte) ([]byte, *protocol.Error) {
	req := Request{}
	err := json.Unmarshal(data, &req)
	if nil != err {
		return nil, protocol.NewError(protocol.E_PARSE, err, nil)
	}

	parts := strings.Split(req.Method, ".")

	si := &serverGrpc.ServerInput{
		Target: parts[0],
		Method: parts[1],
		Params: req.Params,
	}

	c, err := h.pool.Get()
	if nil != err {
		return nil, protocol.NewError(protocol.E_INTERNAL, err, nil)
	}
	defer h.pool.Put(c)
	client := c.(serverGrpc.OverflowApiServerClient)
	out, err := client.Exec(context.Background(), si)
	if err != nil {
		return nil, protocol.NewError(protocol.E_SERVER, err, err)
	}

	res := &Response{
		Result: out.Result,
	}

	r, err := json.Marshal(res)
	if err != nil {
		return nil, protocol.NewError(protocol.E_INTERNAL, err, nil)
	}

	return r, nil
}
