// Code generated by "requestgen -method POST -url /orders -rateLimiter 1+20/2s -type CreateOrderRequest -responseType .CreateOrderResponse"; DO NOT EDIT.

package coinbase

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/c9s/bbgo/pkg/fixedpoint"
	"golang.org/x/time/rate"
	"net/url"
	"reflect"
	"regexp"
)

var CreateOrderRequestLimiter = rate.NewLimiter(10, 1)

func (c *CreateOrderRequest) ProfileID(profileID string) *CreateOrderRequest {
	c.profileID = &profileID
	return c
}

func (c *CreateOrderRequest) OrderType(orderType string) *CreateOrderRequest {
	c.orderType = orderType
	return c
}

func (c *CreateOrderRequest) Side(side string) *CreateOrderRequest {
	c.side = side
	return c
}

func (c *CreateOrderRequest) ProductID(productID string) *CreateOrderRequest {
	c.productID = productID
	return c
}

func (c *CreateOrderRequest) Stp(stp string) *CreateOrderRequest {
	c.stp = &stp
	return c
}

func (c *CreateOrderRequest) Stop(stop string) *CreateOrderRequest {
	c.stop = &stop
	return c
}

func (c *CreateOrderRequest) StopPrice(stopPrice string) *CreateOrderRequest {
	c.stopPrice = &stopPrice
	return c
}

func (c *CreateOrderRequest) Price(price string) *CreateOrderRequest {
	c.price = &price
	return c
}

func (c *CreateOrderRequest) Size(size string) *CreateOrderRequest {
	c.size = &size
	return c
}

func (c *CreateOrderRequest) Funds(funds fixedpoint.Value) *CreateOrderRequest {
	c.funds = &funds
	return c
}

func (c *CreateOrderRequest) TimeInForce(timeInForce string) *CreateOrderRequest {
	c.timeInForce = &timeInForce
	return c
}

func (c *CreateOrderRequest) CancelAfter(cancelAfter string) *CreateOrderRequest {
	c.cancelAfter = &cancelAfter
	return c
}

func (c *CreateOrderRequest) PostOnly(postOnly bool) *CreateOrderRequest {
	c.postOnly = &postOnly
	return c
}

func (c *CreateOrderRequest) ClientOrderID(clientOrderID string) *CreateOrderRequest {
	c.clientOrderID = &clientOrderID
	return c
}

func (c *CreateOrderRequest) MaxFloor(maxFloor string) *CreateOrderRequest {
	c.maxFloor = &maxFloor
	return c
}

func (c *CreateOrderRequest) StopLimitPrice(stopLimitPrice string) *CreateOrderRequest {
	c.stopLimitPrice = &stopLimitPrice
	return c
}

// GetQueryParameters builds and checks the query parameters and returns url.Values
func (c *CreateOrderRequest) GetQueryParameters() (url.Values, error) {
	var params = map[string]interface{}{}

	query := url.Values{}
	for _k, _v := range params {
		query.Add(_k, fmt.Sprintf("%v", _v))
	}

	return query, nil
}

// GetParameters builds and checks the parameters and return the result in a map object
func (c *CreateOrderRequest) GetParameters() (map[string]interface{}, error) {
	var params = map[string]interface{}{}
	// check profileID field -> json key profile_id
	if c.profileID != nil {
		profileID := *c.profileID

		// assign parameter of profileID
		params["profile_id"] = profileID
	} else {
	}
	// check orderType field -> json key type
	orderType := c.orderType

	// TEMPLATE check-required
	if len(orderType) == 0 {
		return nil, fmt.Errorf("type is required, empty string given")
	}
	// END TEMPLATE check-required

	// TEMPLATE check-valid-values
	switch orderType {
	case "limit", "market", "stop":
		params["type"] = orderType

	default:
		return nil, fmt.Errorf("type value %v is invalid", orderType)

	}
	// END TEMPLATE check-valid-values

	// assign parameter of orderType
	params["type"] = orderType
	// check side field -> json key side
	side := c.side

	// TEMPLATE check-required
	if len(side) == 0 {
		return nil, fmt.Errorf("side is required, empty string given")
	}
	// END TEMPLATE check-required

	// TEMPLATE check-valid-values
	switch side {
	case "buy", "sell":
		params["side"] = side

	default:
		return nil, fmt.Errorf("side value %v is invalid", side)

	}
	// END TEMPLATE check-valid-values

	// assign parameter of side
	params["side"] = side
	// check productID field -> json key product_id
	productID := c.productID

	// TEMPLATE check-required
	if len(productID) == 0 {
		return nil, fmt.Errorf("product_id is required, empty string given")
	}
	// END TEMPLATE check-required

	// assign parameter of productID
	params["product_id"] = productID
	// check stp field -> json key stp
	if c.stp != nil {
		stp := *c.stp

		// TEMPLATE check-valid-values
		switch stp {
		case "dc", "co", "cn", "cb":
			params["stp"] = stp

		default:
			return nil, fmt.Errorf("stp value %v is invalid", stp)

		}
		// END TEMPLATE check-valid-values

		// assign parameter of stp
		params["stp"] = stp
	} else {
	}
	// check stop field -> json key stop
	if c.stop != nil {
		stop := *c.stop

		// TEMPLATE check-valid-values
		switch stop {
		case "loss", "entry":
			params["stop"] = stop

		default:
			return nil, fmt.Errorf("stop value %v is invalid", stop)

		}
		// END TEMPLATE check-valid-values

		// assign parameter of stop
		params["stop"] = stop
	} else {
	}
	// check stopPrice field -> json key stop_price
	if c.stopPrice != nil {
		stopPrice := *c.stopPrice

		// assign parameter of stopPrice
		params["stop_price"] = stopPrice
	} else {
	}
	// check price field -> json key price
	if c.price != nil {
		price := *c.price

		// assign parameter of price
		params["price"] = price
	} else {
	}
	// check size field -> json key size
	if c.size != nil {
		size := *c.size

		// assign parameter of size
		params["size"] = size
	} else {
	}
	// check funds field -> json key funds
	if c.funds != nil {
		funds := *c.funds

		// assign parameter of funds
		params["funds"] = funds
	} else {
	}
	// check timeInForce field -> json key time_in_force
	if c.timeInForce != nil {
		timeInForce := *c.timeInForce

		// TEMPLATE check-valid-values
		switch timeInForce {
		case "GTC", "GCC", "IOC", "FOK":
			params["time_in_force"] = timeInForce

		default:
			return nil, fmt.Errorf("time_in_force value %v is invalid", timeInForce)

		}
		// END TEMPLATE check-valid-values

		// assign parameter of timeInForce
		params["time_in_force"] = timeInForce
	} else {
	}
	// check cancelAfter field -> json key cancel_after
	if c.cancelAfter != nil {
		cancelAfter := *c.cancelAfter

		// TEMPLATE check-valid-values
		switch cancelAfter {
		case "min", "hour", "day":
			params["cancel_after"] = cancelAfter

		default:
			return nil, fmt.Errorf("cancel_after value %v is invalid", cancelAfter)

		}
		// END TEMPLATE check-valid-values

		// assign parameter of cancelAfter
		params["cancel_after"] = cancelAfter
	} else {
	}
	// check postOnly field -> json key post_only
	if c.postOnly != nil {
		postOnly := *c.postOnly

		// assign parameter of postOnly
		params["post_only"] = postOnly
	} else {
	}
	// check clientOrderID field -> json key client_oid
	if c.clientOrderID != nil {
		clientOrderID := *c.clientOrderID

		// assign parameter of clientOrderID
		params["client_oid"] = clientOrderID
	} else {
	}
	// check maxFloor field -> json key max_floor
	if c.maxFloor != nil {
		maxFloor := *c.maxFloor

		// assign parameter of maxFloor
		params["max_floor"] = maxFloor
	} else {
	}
	// check stopLimitPrice field -> json key stop_limit_price
	if c.stopLimitPrice != nil {
		stopLimitPrice := *c.stopLimitPrice

		// assign parameter of stopLimitPrice
		params["stop_limit_price"] = stopLimitPrice
	} else {
	}

	return params, nil
}

// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (c *CreateOrderRequest) GetParametersQuery() (url.Values, error) {
	query := url.Values{}

	params, err := c.GetParameters()
	if err != nil {
		return query, err
	}

	for _k, _v := range params {
		if c.isVarSlice(_v) {
			c.iterateSlice(_v, func(it interface{}) {
				query.Add(_k+"[]", fmt.Sprintf("%v", it))
			})
		} else {
			query.Add(_k, fmt.Sprintf("%v", _v))
		}
	}

	return query, nil
}

// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (c *CreateOrderRequest) GetParametersJSON() ([]byte, error) {
	params, err := c.GetParameters()
	if err != nil {
		return nil, err
	}

	return json.Marshal(params)
}

// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (c *CreateOrderRequest) GetSlugParameters() (map[string]interface{}, error) {
	var params = map[string]interface{}{}

	return params, nil
}

func (c *CreateOrderRequest) applySlugsToUrl(url string, slugs map[string]string) string {
	for _k, _v := range slugs {
		needleRE := regexp.MustCompile(":" + _k + "\\b")
		url = needleRE.ReplaceAllString(url, _v)
	}

	return url
}

func (c *CreateOrderRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
	sliceValue := reflect.ValueOf(slice)
	for _i := 0; _i < sliceValue.Len(); _i++ {
		it := sliceValue.Index(_i).Interface()
		_f(it)
	}
}

func (c *CreateOrderRequest) isVarSlice(_v interface{}) bool {
	rt := reflect.TypeOf(_v)
	switch rt.Kind() {
	case reflect.Slice:
		return true
	}
	return false
}

func (c *CreateOrderRequest) GetSlugsMap() (map[string]string, error) {
	slugs := map[string]string{}
	params, err := c.GetSlugParameters()
	if err != nil {
		return slugs, nil
	}

	for _k, _v := range params {
		slugs[_k] = fmt.Sprintf("%v", _v)
	}

	return slugs, nil
}

// GetPath returns the request path of the API
func (c *CreateOrderRequest) GetPath() string {
	return "/orders"
}

// Do generates the request object and send the request object to the API endpoint
func (c *CreateOrderRequest) Do(ctx context.Context) (*CreateOrderResponse, error) {
	if err := CreateOrderRequestLimiter.Wait(ctx); err != nil {
		return nil, err
	}

	params, err := c.GetParameters()
	if err != nil {
		return nil, err
	}
	query := url.Values{}

	var apiURL string

	apiURL = c.GetPath()

	req, err := c.client.NewAuthenticatedRequest(ctx, "POST", apiURL, query, params)
	if err != nil {
		return nil, err
	}

	response, err := c.client.SendRequest(req)
	if err != nil {
		return nil, err
	}

	var apiResponse CreateOrderResponse

	type responseUnmarshaler interface {
		Unmarshal(data []byte) error
	}

	if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
		if err := unmarshaler.Unmarshal(response.Body); err != nil {
			return nil, err
		}
	} else {
		// The line below checks the content type, however, some API server might not send the correct content type header,
		// Hence, this is commented for backward compatibility
		// response.IsJSON()
		if err := response.DecodeJSON(&apiResponse); err != nil {
			return nil, err
		}
	}

	type responseValidator interface {
		Validate() error
	}

	if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
		if err := validator.Validate(); err != nil {
			return nil, err
		}
	}
	return &apiResponse, nil
}
