////////////////////////////////////////////////////////////////////////////////
// Copyright © 2022 Privategrity Corporation                                   /
//                                                                             /
// All rights reserved.                                                        /
////////////////////////////////////////////////////////////////////////////////

package restlike

import (
	jww "github.com/spf13/jwalterweatherman"
	"gitlab.com/elixxir/client/catalog"
	"gitlab.com/elixxir/client/single"
	"gitlab.com/elixxir/crypto/contact"
	"gitlab.com/elixxir/crypto/cyclic"
	"gitlab.com/xx_network/crypto/csprng"
	"google.golang.org/protobuf/proto"
)

// SingleRequest allows for making REST-like requests to a RestServer using single-use messages
// Can be used as stateful or declared inline without state
type SingleRequest struct {
	Net    single.Cmix
	Rng    csprng.Source
	E2eGrp *cyclic.Group
}

// Request provides several Method of sending Data to the given URI
// and blocks until the Message is returned
func (s *SingleRequest) Request(method Method, recipient contact.Contact, path URI,
	content Data, headers *Headers, singleParams single.RequestParams) (*Message, error) {
	// Build the Message
	newMessage := &Message{
		Content: content,
		Headers: headers,
		Method:  uint32(method),
		Uri:     string(path),
	}
	msg, err := proto.Marshal(newMessage)
	if err != nil {
		return nil, err
	}

	// Build callback for the single-use response
	signalChannel := make(chan *Message, 1)
	cb := func(msg *Message) {
		signalChannel <- msg
	}

	// Transmit the Message
	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
	if err != nil {
		return nil, err
	}

	// Block waiting for single-use response
	jww.DEBUG.Printf("Restlike waiting for single-use response from %s...", recipient.ID.String())
	newResponse := <-signalChannel
	jww.DEBUG.Printf("Restlike single-use response received from %s", recipient.ID.String())

	return newResponse, nil
}

// AsyncRequest provides several Method of sending Data to the given URI
// and will return the Message to the given Callback when received
func (s *SingleRequest) AsyncRequest(method Method, recipient contact.Contact, path URI,
	content Data, headers *Headers, cb Callback, singleParams single.RequestParams) error {
	// Build the Message
	newMessage := &Message{
		Content: content,
		Headers: headers,
		Method:  uint32(method),
		Uri:     string(path),
	}
	msg, err := proto.Marshal(newMessage)
	if err != nil {
		return err
	}

	// Transmit the Message
	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
	return err
}