package requests

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/bitly/go-simplejson"
)

// Result is the result of a request created by a Builder
type Result interface {
	Error() error
	StatusCode() int
	Headers() http.Header
	Body() []byte
	UnmarshalInto(interface{}) error
	UnmarshalSimpleJSON() (*simplejson.Json, error)
}

type result struct {
	err      error
	response *http.Response
	body     []byte
}

// Error returns an error from the result if present
func (r *result) Error() error {
	return r.err
}

// StatusCode returns the response's status code
func (r *result) StatusCode() int {
	if r.response != nil {
		return r.response.StatusCode
	}
	return 0
}

// Headers returns the response's headers
func (r *result) Headers() http.Header {
	if r.response != nil {
		return r.response.Header
	}
	return nil
}

// Body returns the response's body
func (r *result) Body() []byte {
	return r.body
}

// UnmarshalInto attempts to unmarshal the response into the given interface.
// The response body is assumed to be JSON.
// The response must have a 200 status otherwise an error will be returned.
func (r *result) UnmarshalInto(into interface{}) error {
	body, err := r.getBodyForUnmarshal()
	if err != nil {
		return err
	}

	if err := json.Unmarshal(body, into); err != nil {
		return fmt.Errorf("error unmarshalling body: %v", err)
	}

	return nil
}

// UnmarshalSimpleJSON performs the request and attempts to unmarshal the response into a
// simplejson.Json. The response body is assume to be JSON.
// The response must have a 200 status otherwise an error will be returned.
func (r *result) UnmarshalSimpleJSON() (*simplejson.Json, error) {
	body, err := r.getBodyForUnmarshal()
	if err != nil {
		return nil, err
	}

	data, err := simplejson.NewJson(body)
	if err != nil {
		return nil, fmt.Errorf("error reading json: %v", err)
	}
	return data, nil
}

// getBodyForUnmarshal returns the body if there wasn't an error and the status
// code was 200.
func (r *result) getBodyForUnmarshal() ([]byte, error) {
	if r.Error() != nil {
		return nil, r.Error()
	}

	// Only unmarshal body if the response was successful
	if r.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("unexpected status \"%d\": %s", r.StatusCode(), r.Body())
	}

	return r.Body(), nil
}
