// Package client provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT.
package client

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

// Defines values for OptimizationResultFlowDirection.
const (
	N0 OptimizationResultFlowDirection = 0
	N1 OptimizationResultFlowDirection = 1
)

// Defines values for OptimizationResultStatus.
const (
	Infeasible OptimizationResultStatus = "Infeasible"
	NotSolved  OptimizationResultStatus = "Not Solved"
	Optimal    OptimizationResultStatus = "Optimal"
	Unbounded  OptimizationResultStatus = "Unbounded"
	Undefined  OptimizationResultStatus = "Undefined"
)

// Defines values for OptimizerStrategyChargingStrategy.
const (
	OptimizerStrategyChargingStrategyAttenuateGridPeaks OptimizerStrategyChargingStrategy = "attenuate_grid_peaks"
	OptimizerStrategyChargingStrategyChargeBeforeExport OptimizerStrategyChargingStrategy = "charge_before_export"
	OptimizerStrategyChargingStrategyNone               OptimizerStrategyChargingStrategy = "none"
)

// Defines values for OptimizerStrategyDischargingStrategy.
const (
	OptimizerStrategyDischargingStrategyDischargeBeforeImport OptimizerStrategyDischargingStrategy = "discharge_before_import"
	OptimizerStrategyDischargingStrategyNone                  OptimizerStrategyDischargingStrategy = "none"
)

// BatteryConfig defines model for BatteryConfig.
type BatteryConfig struct {
	// CMax Maximum charge power in W
	CMax float32 `json:"c_max"`

	// CMin Minimum charge power in W
	CMin float32 `json:"c_min"`

	// ChargeFromGrid Controls whether the battery can be charged from the grid.
	//   - True: The battery can be charged from grid at any time. The actual decision is subject
	//     to the optimization.
	//   - False: (default) The battery cannot be charged while power is retrieved from grid
	ChargeFromGrid bool `json:"charge_from_grid,omitempty"`

	// DMax Maximum discharge power in W
	DMax float32 `json:"d_max"`

	// DischargeToGrid Controls whether the battery can discharge to grid.
	//   - True: The battery can discharge to the grid at any time. The actual decision is
	//     subject to the optimization.
	//   - False: (default) The battery cannot be discharged while power is exported to the grid.
	DischargeToGrid bool `json:"discharge_to_grid,omitempty"`

	// PA Monetary value of the stored energy per Wh at end of time horizon
	PA float32 `json:"p_a"`

	// PDemand Minimum charge demand per time step (Wh)
	PDemand []float32 `json:"p_demand,omitempty"`

	// SGoal Goal state of charge for this battery at each time step (Wh)
	SGoal []float32 `json:"s_goal,omitempty"`

	// SInitial Initial state of charge in Wh
	SInitial float32 `json:"s_initial"`

	// SMax Maximum state of charge in Wh
	SMax float32 `json:"s_max"`

	// SMin Minimum state of charge in Wh
	SMin float32 `json:"s_min"`
}

// BatteryResult defines model for BatteryResult.
type BatteryResult struct {
	// ChargingPower Optimal charging energy at each time step (Wh)
	ChargingPower []float32 `json:"charging_power,omitempty"`

	// DischargingPower Optimal discharging energy at each time step (Wh)
	DischargingPower []float32 `json:"discharging_power,omitempty"`

	// StateOfCharge State of charge at each time step (Wh)
	StateOfCharge []float32 `json:"state_of_charge,omitempty"`
}

// Error defines model for Error.
type Error struct {
	// Message Error message describing what went wrong
	Message string `json:"message,omitempty"`
}

// OptimizationInput defines model for OptimizationInput.
type OptimizationInput struct {
	// Batteries Configuration for all batteries in the system
	Batteries []BatteryConfig `json:"batteries"`

	// EtaC Charging efficiency (0 to 1)
	EtaC float32 `json:"eta_c,omitempty"`

	// EtaD Discharging efficiency (0 to 1)
	EtaD       float32           `json:"eta_d,omitempty"`
	Strategy   OptimizerStrategy `json:"strategy,omitempty"`
	TimeSeries TimeSeries        `json:"time_series"`
}

// OptimizationResult defines model for OptimizationResult.
type OptimizationResult struct {
	// Batteries Optimization results for each battery
	Batteries []BatteryResult `json:"batteries,omitempty"`

	// FlowDirection Binary flow direction at each time step:
	// - 0: Import from grid
	// - 1: Export to grid
	FlowDirection []OptimizationResultFlowDirection `json:"flow_direction,omitempty"`

	// GridExport Energy exported to grid at each time step (Wh)
	GridExport []float32 `json:"grid_export,omitempty"`

	// GridImport Energy imported from grid at each time step (Wh)
	GridImport []float32 `json:"grid_import,omitempty"`

	// ObjectiveValue Optimal objective function value (economic benefit in currency units). Null if not optimal.
	ObjectiveValue float32 `json:"objective_value"`

	// Status Optimization solver status:
	// - Optimal: Problem solved to optimality
	// - Infeasible: No feasible solution exists
	// - Unbounded: Objective function is unbounded
	// - Undefined: Problem status is undefined
	// - Not Solved: Problem was not solved
	Status OptimizationResultStatus `json:"status,omitempty"`
}

// OptimizationResultFlowDirection defines model for OptimizationResult.FlowDirection.
type OptimizationResultFlowDirection int

// OptimizationResultStatus Optimization solver status:
// - Optimal: Problem solved to optimality
// - Infeasible: No feasible solution exists
// - Unbounded: Objective function is unbounded
// - Undefined: Problem status is undefined
// - Not Solved: Problem was not solved
type OptimizationResultStatus string

// OptimizerStrategy defines model for OptimizerStrategy.
type OptimizerStrategy struct {
	// ChargingStrategy Sets a strategy for charging in situations where choices are cost neutral.
	// - none (default): no strategy set
	// - charge_before_export: charge batteries before exporting to grid
	// - attenuate_grid_peaks: charge at times with high solar yield to reduce the grid load
	ChargingStrategy OptimizerStrategyChargingStrategy `json:"charging_strategy,omitempty"`

	// DischargingStrategy Sets a strategy for charging in situations where choices are cost neutral.
	// - none (default): no strategy set
	// - discharge_before_import: discharge batteries before importing from grid
	DischargingStrategy OptimizerStrategyDischargingStrategy `json:"discharging_strategy,omitempty"`
}

// OptimizerStrategyChargingStrategy Sets a strategy for charging in situations where choices are cost neutral.
// - none (default): no strategy set
// - charge_before_export: charge batteries before exporting to grid
// - attenuate_grid_peaks: charge at times with high solar yield to reduce the grid load
type OptimizerStrategyChargingStrategy string

// OptimizerStrategyDischargingStrategy Sets a strategy for charging in situations where choices are cost neutral.
// - none (default): no strategy set
// - discharge_before_import: discharge batteries before importing from grid
type OptimizerStrategyDischargingStrategy string

// TimeSeries defines model for TimeSeries.
type TimeSeries struct {
	// Dt Duration in seconds for each time step (s)
	Dt []int `json:"dt"`

	// Ft Forecasted energy generation (e.g., solar PV) at each time step (Wh)
	Ft []float32 `json:"ft"`

	// Gt Household energy demand at each time step (Wh)
	Gt []float32 `json:"gt"`

	// PE Grid export remuneration per Wh at each time step (currency units/Wh)
	PE []float32 `json:"p_E"`

	// PN Grid import price per Wh at each time step (currency units/Wh)
	PN []float32 `json:"p_N"`
}

// PostOptimizeChargeScheduleJSONRequestBody defines body for PostOptimizeChargeSchedule for application/json ContentType.
type PostOptimizeChargeScheduleJSONRequestBody = OptimizationInput

// RequestEditorFn  is the function signature for the RequestEditor callback function
type RequestEditorFn func(ctx context.Context, req *http.Request) error

// Doer performs HTTP requests.
//
// The standard http.Client implements this interface.
type HttpRequestDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

// Client which conforms to the OpenAPI3 specification for this service.
type Client struct {
	// The endpoint of the server conforming to this interface, with scheme,
	// https://api.deepmap.com for example. This can contain a path relative
	// to the server, such as https://api.deepmap.com/dev-test, and all the
	// paths in the swagger spec will be appended to the server.
	Server string

	// Doer for performing requests, typically a *http.Client with any
	// customized settings, such as certificate chains.
	Client HttpRequestDoer

	// A list of callbacks for modifying requests which are generated before sending over
	// the network.
	RequestEditors []RequestEditorFn
}

// ClientOption allows setting custom parameters during construction
type ClientOption func(*Client) error

// Creates a new Client, with reasonable defaults
func NewClient(server string, opts ...ClientOption) (*Client, error) {
	// create a client with sane default values
	client := Client{
		Server: server,
	}
	// mutate client and add all optional params
	for _, o := range opts {
		if err := o(&client); err != nil {
			return nil, err
		}
	}
	// ensure the server URL always has a trailing slash
	if !strings.HasSuffix(client.Server, "/") {
		client.Server += "/"
	}
	// create httpClient, if not already present
	if client.Client == nil {
		client.Client = &http.Client{}
	}
	return &client, nil
}

// WithHTTPClient allows overriding the default Doer, which is
// automatically created using http.Client. This is useful for tests.
func WithHTTPClient(doer HttpRequestDoer) ClientOption {
	return func(c *Client) error {
		c.Client = doer
		return nil
	}
}

// WithRequestEditorFn allows setting up a callback function, which will be
// called right before sending the request. This can be used to mutate the request.
func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
	return func(c *Client) error {
		c.RequestEditors = append(c.RequestEditors, fn)
		return nil
	}
}

// The interface specification for the client above.
type ClientInterface interface {
	// PostOptimizeChargeScheduleWithBody request with any body
	PostOptimizeChargeScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)

	PostOptimizeChargeSchedule(ctx context.Context, body PostOptimizeChargeScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)

	// GetOptimizeHealth request
	GetOptimizeHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
}

func (c *Client) PostOptimizeChargeScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
	req, err := NewPostOptimizeChargeScheduleRequestWithBody(c.Server, contentType, body)
	if err != nil {
		return nil, err
	}
	req = req.WithContext(ctx)
	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
		return nil, err
	}
	return c.Client.Do(req)
}

func (c *Client) PostOptimizeChargeSchedule(ctx context.Context, body PostOptimizeChargeScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
	req, err := NewPostOptimizeChargeScheduleRequest(c.Server, body)
	if err != nil {
		return nil, err
	}
	req = req.WithContext(ctx)
	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
		return nil, err
	}
	return c.Client.Do(req)
}

func (c *Client) GetOptimizeHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
	req, err := NewGetOptimizeHealthRequest(c.Server)
	if err != nil {
		return nil, err
	}
	req = req.WithContext(ctx)
	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
		return nil, err
	}
	return c.Client.Do(req)
}

// NewPostOptimizeChargeScheduleRequest calls the generic PostOptimizeChargeSchedule builder with application/json body
func NewPostOptimizeChargeScheduleRequest(server string, body PostOptimizeChargeScheduleJSONRequestBody) (*http.Request, error) {
	var bodyReader io.Reader
	buf, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}
	bodyReader = bytes.NewReader(buf)
	return NewPostOptimizeChargeScheduleRequestWithBody(server, "application/json", bodyReader)
}

// NewPostOptimizeChargeScheduleRequestWithBody generates requests for PostOptimizeChargeSchedule with any type of body
func NewPostOptimizeChargeScheduleRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
	var err error

	serverURL, err := url.Parse(server)
	if err != nil {
		return nil, err
	}

	operationPath := fmt.Sprintf("/optimize/charge-schedule")
	if operationPath[0] == '/' {
		operationPath = "." + operationPath
	}

	queryURL, err := serverURL.Parse(operationPath)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", queryURL.String(), body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", contentType)

	return req, nil
}

// NewGetOptimizeHealthRequest generates requests for GetOptimizeHealth
func NewGetOptimizeHealthRequest(server string) (*http.Request, error) {
	var err error

	serverURL, err := url.Parse(server)
	if err != nil {
		return nil, err
	}

	operationPath := fmt.Sprintf("/optimize/health")
	if operationPath[0] == '/' {
		operationPath = "." + operationPath
	}

	queryURL, err := serverURL.Parse(operationPath)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", queryURL.String(), nil)
	if err != nil {
		return nil, err
	}

	return req, nil
}

func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
	for _, r := range c.RequestEditors {
		if err := r(ctx, req); err != nil {
			return err
		}
	}
	for _, r := range additionalEditors {
		if err := r(ctx, req); err != nil {
			return err
		}
	}
	return nil
}

// ClientWithResponses builds on ClientInterface to offer response payloads
type ClientWithResponses struct {
	ClientInterface
}

// NewClientWithResponses creates a new ClientWithResponses, which wraps
// Client with return type handling
func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
	client, err := NewClient(server, opts...)
	if err != nil {
		return nil, err
	}
	return &ClientWithResponses{client}, nil
}

// WithBaseURL overrides the baseURL.
func WithBaseURL(baseURL string) ClientOption {
	return func(c *Client) error {
		newBaseURL, err := url.Parse(baseURL)
		if err != nil {
			return err
		}
		c.Server = newBaseURL.String()
		return nil
	}
}

// ClientWithResponsesInterface is the interface specification for the client with responses above.
type ClientWithResponsesInterface interface {
	// PostOptimizeChargeScheduleWithBodyWithResponse request with any body
	PostOptimizeChargeScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostOptimizeChargeScheduleResponse, error)

	PostOptimizeChargeScheduleWithResponse(ctx context.Context, body PostOptimizeChargeScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*PostOptimizeChargeScheduleResponse, error)

	// GetOptimizeHealthWithResponse request
	GetOptimizeHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetOptimizeHealthResponse, error)
}

type PostOptimizeChargeScheduleResponse struct {
	Body         []byte
	HTTPResponse *http.Response
	JSON200      *OptimizationResult
	JSON400      *Error
	JSON500      *Error
}

// Status returns HTTPResponse.Status
func (r PostOptimizeChargeScheduleResponse) Status() string {
	if r.HTTPResponse != nil {
		return r.HTTPResponse.Status
	}
	return http.StatusText(0)
}

// StatusCode returns HTTPResponse.StatusCode
func (r PostOptimizeChargeScheduleResponse) StatusCode() int {
	if r.HTTPResponse != nil {
		return r.HTTPResponse.StatusCode
	}
	return 0
}

type GetOptimizeHealthResponse struct {
	Body         []byte
	HTTPResponse *http.Response
	JSON200      *struct {
		Message string `json:"message,omitempty"`
		Status  string `json:"status,omitempty"`
	}
}

// Status returns HTTPResponse.Status
func (r GetOptimizeHealthResponse) Status() string {
	if r.HTTPResponse != nil {
		return r.HTTPResponse.Status
	}
	return http.StatusText(0)
}

// StatusCode returns HTTPResponse.StatusCode
func (r GetOptimizeHealthResponse) StatusCode() int {
	if r.HTTPResponse != nil {
		return r.HTTPResponse.StatusCode
	}
	return 0
}

// PostOptimizeChargeScheduleWithBodyWithResponse request with arbitrary body returning *PostOptimizeChargeScheduleResponse
func (c *ClientWithResponses) PostOptimizeChargeScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostOptimizeChargeScheduleResponse, error) {
	rsp, err := c.PostOptimizeChargeScheduleWithBody(ctx, contentType, body, reqEditors...)
	if err != nil {
		return nil, err
	}
	return ParsePostOptimizeChargeScheduleResponse(rsp)
}

func (c *ClientWithResponses) PostOptimizeChargeScheduleWithResponse(ctx context.Context, body PostOptimizeChargeScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*PostOptimizeChargeScheduleResponse, error) {
	rsp, err := c.PostOptimizeChargeSchedule(ctx, body, reqEditors...)
	if err != nil {
		return nil, err
	}
	return ParsePostOptimizeChargeScheduleResponse(rsp)
}

// GetOptimizeHealthWithResponse request returning *GetOptimizeHealthResponse
func (c *ClientWithResponses) GetOptimizeHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetOptimizeHealthResponse, error) {
	rsp, err := c.GetOptimizeHealth(ctx, reqEditors...)
	if err != nil {
		return nil, err
	}
	return ParseGetOptimizeHealthResponse(rsp)
}

// ParsePostOptimizeChargeScheduleResponse parses an HTTP response from a PostOptimizeChargeScheduleWithResponse call
func ParsePostOptimizeChargeScheduleResponse(rsp *http.Response) (*PostOptimizeChargeScheduleResponse, error) {
	bodyBytes, err := io.ReadAll(rsp.Body)
	defer func() { _ = rsp.Body.Close() }()
	if err != nil {
		return nil, err
	}

	response := &PostOptimizeChargeScheduleResponse{
		Body:         bodyBytes,
		HTTPResponse: rsp,
	}

	switch {
	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
		var dest OptimizationResult
		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
			return nil, err
		}
		response.JSON200 = &dest

	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
		var dest Error
		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
			return nil, err
		}
		response.JSON400 = &dest

	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
		var dest Error
		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
			return nil, err
		}
		response.JSON500 = &dest

	}

	return response, nil
}

// ParseGetOptimizeHealthResponse parses an HTTP response from a GetOptimizeHealthWithResponse call
func ParseGetOptimizeHealthResponse(rsp *http.Response) (*GetOptimizeHealthResponse, error) {
	bodyBytes, err := io.ReadAll(rsp.Body)
	defer func() { _ = rsp.Body.Close() }()
	if err != nil {
		return nil, err
	}

	response := &GetOptimizeHealthResponse{
		Body:         bodyBytes,
		HTTPResponse: rsp,
	}

	switch {
	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
		var dest struct {
			Message string `json:"message,omitempty"`
			Status  string `json:"status,omitempty"`
		}
		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
			return nil, err
		}
		response.JSON200 = &dest

	}

	return response, nil
}
