// package awsutils provides a helper for Chitin-ifying an AWS service configuration
package awsutils

import (
	"fmt"
	"net"
	"net/http"
	"time"

	"code.justin.tv/common/chitin"
	"github.com/aws/aws-sdk-go/aws"
	"golang.org/x/net/context"
)

const (
	// We don't want our API calls to take an unbounded amount of time
	// if the AWS API (or our connection to it) is acting up.
	httpRequestTimeout = 10 * time.Second
)

var (
	awsTransport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		// allow many idle connections per host, vs the default of two
		MaxIdleConnsPerHost: 1000,
	}
)

// WithChitin configures an aws.Config to use an HTTP client that will
// cancel requests when the context is canceled. Additionally, it will
// configure the transport to maintain a large pool of keepalive
// connections, which will avoid spending a ton of time in TLS
// negotiation.
//
// The chitin API we call here returns an error if the
// http.RoundTripper doesn't implement the required additional methods
// CancelRequest and CloseIdleConnections. Since we're passing in a
// *http.Transport, we know we won't get an error. Panic if that
// somehow changes.
//
// The values for Timeout and KeepAlive in the dialer and for
// TLSHandshakeTimeout in the transport are taken from the values used
// in net/http.DefaultTransport.
func WithChitin(ctx context.Context, cfg *aws.Config) *aws.Config {
	var rt http.RoundTripper
	if cfg != nil && cfg.HTTPClient != nil {
		rt = cfg.HTTPClient.Transport
	}
	if rt == nil {
		rt = awsTransport
	}

	crt, err := chitin.RoundTripper(ctx, rt)
	if err != nil {
		panic(fmt.Sprintf("chitin.RoundTripper rejected %T", rt))
	}
	rt = crt

	newClient := new(http.Client)
	if cfg != nil && cfg.HTTPClient != nil {
		*newClient = *cfg.HTTPClient
	} else if http.DefaultClient != nil {
		*newClient = *http.DefaultClient
	}

	newClient.Transport = rt
	newClient.Timeout = httpRequestTimeout

	return cfg.WithHTTPClient(newClient)
}
